1use mittens_engine::{engine, utils};
2
3#[path = "example_util/mod.rs"]
4mod example_util;
5
6use mittens_engine::engine::ecs::component::{
7 AmbientLightComponent, BackgroundColorComponent, Camera3DComponent, ColorComponent,
8 InputComponent, InputTransformModeComponent, OpacityComponent, PointLightComponent,
9 RenderableComponent, TextComponent, TextureFilteringComponent, TransformComponent,
10};
11
12fn spawn_cube(
13 universe: &mut engine::Universe,
14 parent: engine::ecs::ComponentId,
15 position: (f32, f32, f32),
16 scale: (f32, f32, f32),
17 color: Option<(f32, f32, f32, f32)>,
18 opacity: Option<OpacityComponent>,
19) {
20 let t = universe.world.add_component(
21 TransformComponent::new()
22 .with_position(position.0, position.1, position.2)
23 .with_scale(scale.0, scale.1, scale.2),
24 );
25 let r = universe.world.add_component(RenderableComponent::cube());
26
27 let _ = universe.attach(parent, t);
28 let _ = universe.attach(t, r);
29
30 if let Some((cr, cg, cb, ca)) = color {
31 let c = universe
32 .world
33 .add_component(ColorComponent::rgba(cr, cg, cb, ca));
34 let _ = universe.attach(r, c);
35 }
36
37 if let Some(o) = opacity {
38 let o = universe.world.add_component(o);
39 let _ = universe.attach(r, o);
40 }
41}
42
43fn spawn_text_label(universe: &mut engine::Universe, position: (f32, f32, f32), text: &str) {
44 let text_root = universe
46 .world
47 .add_component(TransformComponent::new().with_position(position.0, position.1, position.2));
48
49 let text_scale = universe
50 .world
51 .add_component(TransformComponent::new().with_scale(0.18, 0.18, 1.0));
52 let _ = universe.attach(text_root, text_scale);
53
54 let text_c = universe.world.add_component(TextComponent::new(text));
55 let _ = universe.attach(text_scale, text_c);
56
57 let filtering = universe
59 .world
60 .add_component(TextureFilteringComponent::nearest());
61 let _ = universe.attach(text_c, filtering);
62
63 let color = universe
65 .world
66 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
67 let _ = universe.attach(text_c, color);
68
69 universe.add(text_root);
70}
71
72fn text_block_dimensions(text: &str) -> (usize, usize) {
73 let mut max_cols = 0usize;
74 let mut rows = 1usize;
75 let mut cur = 0usize;
76
77 for ch in text.chars() {
78 if ch == '\n' {
79 max_cols = max_cols.max(cur);
80 cur = 0;
81 rows += 1;
82 } else {
83 cur += 1;
84 }
85 }
86 max_cols = max_cols.max(cur);
87
88 (max_cols.max(1), rows.max(1))
89}
90
91fn spawn_text_label_with_bg(
92 universe: &mut engine::Universe,
93 position: (f32, f32, f32),
94 text: &str,
95 bg_opacity: f32,
96) {
97 let text_root = universe
103 .world
104 .add_component(TransformComponent::new().with_position(position.0, position.1, position.2));
105
106 let (cols, rows) = text_block_dimensions(text);
108 let text_scale = 0.18_f32;
109 let pad_x = 0.55_f32;
110 let pad_y = 0.45_f32;
111 let bg_w = cols as f32 * text_scale + pad_x;
112 let bg_h = rows as f32 * text_scale + pad_y;
113
114 let bg_t = universe.world.add_component(
115 TransformComponent::new()
116 .with_position(1.5, 0.0, -0.02)
117 .with_scale(bg_w, bg_h, 1.0),
118 );
119 let bg_r = universe.world.add_component(RenderableComponent::square());
120 let _ = universe.attach(text_root, bg_t);
121 let _ = universe.attach(bg_t, bg_r);
122
123 let bg_c = universe
124 .world
125 .add_component(ColorComponent::rgba(0.0, 0.0, 0.0, 1.0));
126 let _ = universe.attach(bg_r, bg_c);
127
128 let bg_o = universe
129 .world
130 .add_component(OpacityComponent::new().with_opacity(bg_opacity));
131 let _ = universe.attach(bg_r, bg_o);
132
133 let text_scale_t = universe
134 .world
135 .add_component(TransformComponent::new().with_scale(text_scale, text_scale, 1.0));
136 let _ = universe.attach(text_root, text_scale_t);
137
138 let text_c = universe.world.add_component(TextComponent::new(text));
139 let _ = universe.attach(text_scale_t, text_c);
140
141 let filtering = universe
143 .world
144 .add_component(TextureFilteringComponent::nearest());
145 let _ = universe.attach(text_c, filtering);
146
147 let color = universe
150 .world
151 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 0.998));
152 let _ = universe.attach(text_c, color);
153
154 universe.add(text_root);
155}
156
157fn spawn_demo_spot(
158 universe: &mut engine::Universe,
159 spot_pos: (f32, f32, f32),
160 opacity: f32,
161 multiple_layers: bool,
162 grid_n: i32,
163) {
164 let spot = universe
165 .world
166 .add_component(TransformComponent::new().with_position(spot_pos.0, spot_pos.1, spot_pos.2));
167
168 let white = universe
170 .world
171 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
172 let _ = universe.attach(spot, white);
173
174 let mut oc = OpacityComponent::new().with_opacity(opacity);
176 if multiple_layers {
177 oc = oc.with_multiple_layers();
178 }
179 let oc = universe.world.add_component(oc);
180 let _ = universe.attach(spot, oc);
181
182 universe.add(spot);
183
184 let n = grid_n.max(1);
186 let spacing = if n <= 2 { 1.0 } else { 0.6 };
187 let cube_scale = if n <= 2 { 0.8 } else { 0.45 };
188 let half = (n as f32 - 1.0) * spacing * 0.5;
189
190 for z in 0..n {
191 for y in 0..n {
192 for x in 0..n {
193 let px = x as f32 * spacing - half;
194 let py = y as f32 * spacing;
195 let pz = z as f32 * spacing - half;
196
197 spawn_cube(
198 universe,
199 spot,
200 (px, py, pz),
201 (cube_scale, cube_scale, cube_scale),
202 None,
203 None,
204 );
205 }
206 }
207 }
208
209 let label = format!(
211 "opacity: {:.2}\nmulti-layer: {}",
212 opacity,
213 if multiple_layers { "true" } else { "false" }
214 );
215 let label_y = spot_pos.1 + (n as f32 * spacing) + 1.8;
217 let label_x = spot_pos.0 - (half + 0.6);
218 let label_z = spot_pos.2 - (half + 1.0);
219 spawn_text_label(universe, (label_x, label_y, label_z), &label);
220}
221
222fn spawn_demo_strip_pair(
223 universe: &mut engine::Universe,
224 spot_pos: (f32, f32, f32),
225 transparent_multiple_layers: bool,
226) {
227 let n_z: i32 = 4;
228 let spacing = 1.0;
229 let cube_scale = 0.8;
230 let half_z = (n_z as f32 - 1.0) * spacing * 0.5;
231
232 let transparent_root = universe
234 .world
235 .add_component(TransformComponent::new().with_position(spot_pos.0, spot_pos.1, spot_pos.2));
236
237 let white = universe
238 .world
239 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
240 let _ = universe.attach(transparent_root, white);
241
242 let mut oc = OpacityComponent::new().with_opacity(0.50);
243 if transparent_multiple_layers {
244 oc = oc.with_multiple_layers();
245 }
246 let oc = universe.world.add_component(oc);
247 let _ = universe.attach(transparent_root, oc);
248
249 universe.add(transparent_root);
250
251 for z in 0..n_z {
252 let pz = z as f32 * spacing - half_z;
253 spawn_cube(
254 universe,
255 transparent_root,
256 (0.0, 0.0, pz),
257 (cube_scale, cube_scale, cube_scale),
258 None,
259 None,
260 );
261 }
262
263 let opaque_root = universe
265 .world
266 .add_component(TransformComponent::new().with_position(
267 spot_pos.0 - spacing,
268 spot_pos.1,
269 spot_pos.2,
270 ));
271 let white = universe
272 .world
273 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
274 let _ = universe.attach(opaque_root, white);
275 universe.add(opaque_root);
276
277 for z in 0..n_z {
278 let pz = z as f32 * spacing - half_z;
279 spawn_cube(
280 universe,
281 opaque_root,
282 (0.0, 0.0, pz),
283 (cube_scale, cube_scale, cube_scale),
284 None,
285 None,
286 );
287 }
288
289 let label = format!(
290 "mini: opaque + transparent strip\ntransparent opacity: 0.50\nmulti-layer: {}",
291 if transparent_multiple_layers {
292 "true"
293 } else {
294 "false"
295 }
296 );
297 let label_y = spot_pos.1 + spacing + 1.8;
298 let label_x = spot_pos.0 - (spacing * 2.6);
299 let label_z = spot_pos.2 - (half_z + 1.0);
300 spawn_text_label(universe, (label_x, label_y, label_z), &label);
301}
302
303fn spawn_demo_xy_plane(
304 universe: &mut engine::Universe,
305 spot_pos: (f32, f32, f32),
306 opacity: f32,
307 n_x: i32,
308 n_y: i32,
309 z: f32,
310) {
311 let spot = universe
312 .world
313 .add_component(TransformComponent::new().with_position(spot_pos.0, spot_pos.1, spot_pos.2));
314
315 let white = universe
316 .world
317 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
318 let _ = universe.attach(spot, white);
319
320 let oc = universe
321 .world
322 .add_component(OpacityComponent::new().with_opacity(opacity));
323 let _ = universe.attach(spot, oc);
324
325 universe.add(spot);
326
327 let nx = n_x.max(1);
328 let ny = n_y.max(1);
329 let spacing = 0.45;
330 let cube_scale = 0.35;
331 let half_x = (nx as f32 - 1.0) * spacing * 0.5;
332
333 for y in 0..ny {
334 for x in 0..nx {
335 let px = x as f32 * spacing - half_x;
336 let py = y as f32 * spacing;
337 spawn_cube(
338 universe,
339 spot,
340 (px, py, z),
341 (cube_scale, cube_scale, cube_scale),
342 None,
343 None,
344 );
345 }
346 }
347
348 let label = format!(
350 "XY plane: {}x{}\nopacity: {:.2}\nmulti-layer: false",
351 nx, ny, opacity
352 );
353 let label_x = spot_pos.0 - (half_x + 0.6);
354 let label_y = spot_pos.1 + (ny as f32 * spacing) + 1.2;
355 let label_z = spot_pos.2 - 2.5;
356 spawn_text_label_with_bg(universe, (label_x, label_y, label_z), &label, 0.50);
357}
358
359fn main() {
360 mittens_engine::example_support::ensure_model_assets();
361 utils::logger::init();
362
363 let world = engine::ecs::World::default();
364 let mut universe = engine::Universe::new(world);
365
366 let bg = universe
368 .world
369 .add_component(BackgroundColorComponent::new());
370 let bg_c = universe
371 .world
372 .add_component(ColorComponent::rgba(0.22, 0.08, 0.10, 1.0));
373 let _ = universe.world.add_child(bg, bg_c);
374 universe.add(bg);
375
376 let ambient = universe
378 .world
379 .add_component(AmbientLightComponent::rgb(0.35, 0.35, 0.40));
380 universe.add(ambient);
381
382 let light_t = universe.world.add_component(
384 TransformComponent::new()
385 .with_position(0.0, 18.0, 10.0)
386 .with_scale(0.2, 0.2, 0.2),
387 );
388 let light = universe.world.add_component(
389 PointLightComponent::new()
390 .with_color(1.0, 1.0, 1.0)
391 .with_intensity(1.6)
392 .with_distance(80.0),
393 );
394 let _ = universe.attach(light_t, light);
395 universe.add(light_t);
396
397 let input = universe
400 .world
401 .add_component(InputComponent::new().with_speed(3.0));
402 let input_mode = universe.world.add_component(
403 InputTransformModeComponent::forward_z()
404 .with_roll_axis_y()
405 .with_fps_rotation(),
406 );
407 let _ = universe.attach(input, input_mode);
408
409 let rig_transform = universe
410 .world
411 .add_component(TransformComponent::new().with_position(0.0, 6.0, 20.0));
412 let _ = universe.attach(input, rig_transform);
413
414 let camera = universe.world.add_component(Camera3DComponent::new());
415 let _ = universe.attach(rig_transform, camera);
416
417 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
419
420 universe.add(input);
421
422 let ground = universe.world.add_component(
424 TransformComponent::new()
425 .with_position(0.0, -0.6, 0.0)
426 .with_scale(60.0, 1.0, 60.0),
427 );
428 let ground_r = universe.world.add_component(RenderableComponent::cube());
429 let ground_c = universe
430 .world
431 .add_component(ColorComponent::rgba(0.95, 0.95, 0.95, 1.0));
432 let _ = universe.attach(ground, ground_r);
433 let _ = universe.attach(ground_r, ground_c);
434 universe.add(ground);
435
436 for i in 0..6 {
438 let x = -10.0 + i as f32 * 4.0;
439 spawn_cube(
440 &mut universe,
441 ground,
442 (x, 1.2, -18.0),
443 (2.0, 2.0, 2.0),
444 Some((1.0, 0.92, 0.15, 1.0)),
445 None,
446 );
447 }
448
449 spawn_demo_xy_plane(&mut universe, (-14.0, 0.0, 0.0), 0.50, 16, 16, 0.0);
452
453 spawn_demo_spot(&mut universe, (-4.0, 0.0, 0.0), 0.50, false, 8);
456
457 spawn_demo_spot(&mut universe, (4.0, 0.0, 0.0), 0.50, true, 8);
459
460 spawn_demo_strip_pair(&mut universe, (-4.0, 0.0, 10.0), false);
462 spawn_demo_strip_pair(&mut universe, (4.0, 0.0, 10.0), true);
463
464 universe.systems.process_commands(
466 &mut universe.world,
467 &mut universe.visuals,
468 &mut universe.render_assets,
469 &mut universe.command_queue,
470 );
471
472 universe.enable_repl();
473
474 engine::Windowing::run_app(universe).expect("Windowing failed");
475}