pub struct Camera3DComponent {
pub enabled: bool,
pub handle: Option<CameraHandle>,
pub component_id: Option<ComponentId>,
pub target: CameraTarget,
pub fov_y_degrees: f32,
pub z_near: f32,
pub z_far: f32,
}Expand description
3D camera component.
Contract:
- On init, registers a camera with
CameraSystem. - The most recently registered camera becomes active.
- Call
make_active_camera()to explicitly set this camera active.
Fields§
§enabled: bool§handle: Option<CameraHandle>§component_id: Option<ComponentId>§target: CameraTargetWhich output this camera targets for activation.
Notes:
- Today, the 3D camera drives the Window camera matrices.
- This field exists so
make_active_camera()can be target-aware.
fov_y_degrees: f32Vertical field of view (degrees).
z_near: f32§z_far: f32Implementations§
Source§impl Camera3DComponent
impl Camera3DComponent
pub const DEFAULT_FOV_Y_DEGREES: f32 = 60.0
pub const DEFAULT_Z_NEAR: f32 = 0.1
pub const DEFAULT_Z_FAR: f32 = 150.0
Sourcepub fn new() -> Self
pub fn new() -> Self
Examples found in repository?
examples/example_util/mod.rs (line 48)
15pub fn spawn_mms_demo_rig(
16 universe: &mut engine::Universe,
17 cam_pos: [f32; 3],
18) -> engine::ecs::ComponentId {
19 use engine::ecs::component::{
20 BackgroundColorComponent, Camera3DComponent, InputComponent, InputTransformModeComponent,
21 TransformComponent,
22 };
23
24 // Dark blue clear colour.
25 let bg_color = universe
26 .world
27 .add_component(BackgroundColorComponent::new());
28 let bg_color_c = universe
29 .world
30 .add_component(ColorComponent::rgba(0.02, 0.03, 0.10, 1.0));
31 let _ = universe.world.add_child(bg_color, bg_color_c);
32 universe.add(bg_color);
33
34 // Camera rig: Input → Transform → Camera3D.
35 let input = universe
36 .world
37 .add_component(InputComponent::new().with_speed(3.0));
38 let input_mode = universe
39 .world
40 .add_component(InputTransformModeComponent::forward_z().with_roll_axis_y());
41 let _ = universe.attach(input, input_mode);
42
43 let cam_transform = universe
44 .world
45 .add_component(TransformComponent::new().with_position(cam_pos[0], cam_pos[1], cam_pos[2]));
46 let _ = universe.attach(input, cam_transform);
47
48 let camera = universe.world.add_component(Camera3DComponent::new());
49 let _ = universe.attach(cam_transform, camera);
50
51 universe.add(input);
52
53 spawn_desktop_camera_controls_hint(universe, cam_transform);
54
55 cam_transform
56}More examples
examples/audio-clip-instance-demo.rs (line 34)
13fn main() {
14 mittens_engine::example_support::ensure_model_assets();
15 utils::logger::init();
16
17 println!("[audio-clip-instance-demo] start");
18
19 let world = engine::ecs::World::default();
20 let mut universe = engine::Universe::new(world);
21
22 // Minimal camera so the window opens.
23 let input = universe
24 .world
25 .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
26 let rig = universe.world.add_component(
27 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 3.0),
28 );
29 let input_mode = universe.world.add_component(
30 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
31 );
32 let cam = universe
33 .world
34 .add_component(engine::ecs::component::Camera3DComponent::new().with_fov(60.0));
35 let _ = universe.attach(input, input_mode);
36 let _ = universe.attach(input, rig);
37 let _ = universe.attach(rig, cam);
38 universe.add(input);
39
40 let clear = universe
41 .world
42 .add_component(engine::ecs::component::BackgroundColorComponent::new());
43 let clear_c = universe
44 .world
45 .add_component(engine::ecs::component::ColorComponent::rgba(
46 0.06, 0.07, 0.10, 1.0,
47 ));
48 let _ = universe.world.add_child(clear, clear_c);
49 universe.add(clear);
50
51 let source = include_str!("audio-clip-instance-demo.mms");
52 let output = scripting::MeowMeowRunner::eval_with_world_at_path(
53 source,
54 Some("examples/audio-clip-instance-demo.mms"),
55 &mut universe.world,
56 &mut universe.systems.rx,
57 &mut universe.command_queue,
58 );
59
60 for error in &output.errors {
61 eprintln!("[mms] {error}");
62 }
63 println!(
64 "[audio-clip-instance-demo] mms ok: {} intent(s)",
65 output.intents.len()
66 );
67
68 for intent in output.intents {
69 universe
70 .command_queue
71 .push_intent_now(engine::ecs::ComponentId::default(), intent);
72 }
73
74 universe.systems.process_commands(
75 &mut universe.world,
76 &mut universe.visuals,
77 &mut universe.render_assets,
78 &mut universe.command_queue,
79 );
80
81 universe.enable_repl();
82 engine::Windowing::run_app(universe).expect("Windowing failed");
83}examples/audio-music-demo.rs (line 32)
11fn main() {
12 mittens_engine::example_support::ensure_model_assets();
13 utils::logger::init();
14
15 println!("[audio-music-demo] start");
16
17 let world = engine::ecs::World::default();
18 let mut universe = engine::Universe::new(world);
19
20 // Minimal camera so the window opens.
21 let input = universe
22 .world
23 .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
24 let rig = universe.world.add_component(
25 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 3.0),
26 );
27 let input_mode = universe.world.add_component(
28 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
29 );
30 let cam = universe
31 .world
32 .add_component(engine::ecs::component::Camera3DComponent::new().with_fov(60.0));
33 let _ = universe.attach(input, input_mode);
34 let _ = universe.attach(input, rig);
35 let _ = universe.attach(rig, cam);
36 universe.add(input);
37
38 // Dim clear color so console output stays readable in case of errors.
39 let clear = universe
40 .world
41 .add_component(engine::ecs::component::BackgroundColorComponent::new());
42 let clear_c = universe
43 .world
44 .add_component(engine::ecs::component::ColorComponent::rgba(
45 0.06, 0.07, 0.10, 1.0,
46 ));
47 let _ = universe.world.add_child(clear, clear_c);
48 universe.add(clear);
49
50 let source = include_str!("audio-music-demo.mms");
51 let output = scripting::MeowMeowRunner::eval_with_world_at_path(
52 source,
53 Some("examples/audio-music-demo.mms"),
54 &mut universe.world,
55 &mut universe.systems.rx,
56 &mut universe.command_queue,
57 );
58
59 for error in &output.errors {
60 eprintln!("[mms] {error}");
61 }
62 println!(
63 "[audio-music-demo] mms ok: {} intent(s)",
64 output.intents.len()
65 );
66
67 for intent in output.intents {
68 universe
69 .command_queue
70 .push_intent_now(engine::ecs::ComponentId::default(), intent);
71 }
72
73 universe.systems.process_commands(
74 &mut universe.world,
75 &mut universe.visuals,
76 &mut universe.render_assets,
77 &mut universe.command_queue,
78 );
79
80 universe.enable_repl();
81 engine::Windowing::run_app(universe).expect("Windowing failed");
82}examples/opacity-example.rs (line 414)
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 // Dark brown / pink background.
367 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 // Ambient so unlit areas aren't black.
377 let ambient = universe
378 .world
379 .add_component(AmbientLightComponent::rgb(0.35, 0.35, 0.40));
380 universe.add(ambient);
381
382 // A bright overhead light.
383 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 // --- Camera rig ---
398 // I { T { C3D { with_fps_rotation with_roll_axis_y } } }
399 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 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
418 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
419
420 universe.add(input);
421
422 // --- Ground ---
423 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 // --- Opaque yellow cubes behind (contrast reference) ---
437 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 // --- Demo spots ---
450 // Left of the left big spot: a single-layer transparent XY plane (16x16).
451 spawn_demo_xy_plane(&mut universe, (-14.0, 0.0, 0.0), 0.50, 16, 16, 0.0);
452
453 // Big spots: 8x8x8
454 // Left: single-layer transparent (instanced)
455 spawn_demo_spot(&mut universe, (-4.0, 0.0, 0.0), 0.50, false, 8);
456
457 // Right: multi-layer transparent (sorted)
458 spawn_demo_spot(&mut universe, (4.0, 0.0, 0.0), 0.50, true, 8);
459
460 // Small spots: opaque 1x4 strip + transparent 1x4 strip (along Z).
461 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 // Process init-time registrations.
465 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}examples/pointer-events.rs (line 403)
349fn main() {
350 mittens_engine::example_support::ensure_model_assets();
351 utils::logger::init();
352
353 let world = engine::ecs::World::default();
354 let mut universe = engine::Universe::new(world);
355
356 use engine::ecs::component::{
357 AmbientLightComponent, BackgroundColorComponent, Camera3DComponent, ColorComponent,
358 DirectionalLightComponent, InputComponent, InputTransformModeComponent, PointerComponent,
359 TransformComponent,
360 };
361
362 // Background.
363 let bg = universe
364 .world
365 .add_component(BackgroundColorComponent::new());
366 let bg_c = universe
367 .world
368 .add_component(ColorComponent::rgba(0.12, 0.12, 0.16, 1.0));
369 let _ = universe.world.add_child(bg, bg_c);
370 universe.add(bg);
371
372 // Lighting.
373 let ambient = universe
374 .world
375 .add_component(AmbientLightComponent::rgb(0.30, 0.30, 0.32));
376 universe.add(ambient);
377
378 let sun_t = universe
379 .world
380 .add_component(TransformComponent::new().with_position(2.0, 4.0, 3.0));
381 let sun = universe
382 .world
383 .add_component(DirectionalLightComponent::new());
384 let _ = universe.attach(sun_t, sun);
385 universe.add(sun_t);
386
387 // Camera + pointer rig.
388 let input = universe
389 .world
390 .add_component(InputComponent::new().with_speed(3.0));
391 let input_mode = universe.world.add_component(
392 InputTransformModeComponent::forward_z()
393 .with_fps_rotation()
394 .with_roll_axis_y(),
395 );
396 let _ = universe.attach(input, input_mode);
397
398 let cam_t = universe
399 .world
400 .add_component(TransformComponent::new().with_position(0.0, 0.5, 4.5));
401 let cam = universe
402 .world
403 .add_component(Camera3DComponent::new().with_fov(70.0));
404 let pointer = universe.world.add_component(PointerComponent::new());
405
406 let _ = universe.attach(input, cam_t);
407 let _ = universe.attach(cam_t, cam);
408 let _ = universe.attach(cam, pointer);
409
410 example_util::spawn_desktop_camera_controls_hint(&mut universe, cam_t);
411 universe.add(input);
412
413 // Scene root for all interactive objects.
414 let scene = universe.world.add_component(TransformComponent::new());
415 universe.add(scene);
416
417 // --- LEFT: click-only cubes ---
418 let click_group = universe
419 .world
420 .add_component(TransformComponent::new().with_position(-2.2, 0.0, 0.0));
421 let _ = universe.attach(scene, click_group);
422
423 for (i, dy) in [-0.55_f32, 0.0, 0.55].iter().enumerate() {
424 spawn_click_cube(&mut universe, click_group, [0.0, *dy, 0.0]);
425 let _ = i;
426 }
427 spawn_label(
428 &mut universe,
429 click_group,
430 [-0.25, -1.05, 0.0],
431 "click\ncycles color",
432 );
433
434 // --- MID: drag-only cube ---
435 let drag_cube_root = universe
436 .world
437 .add_component(TransformComponent::new().with_position(0.0, 0.0, 0.0));
438 let _ = universe.attach(scene, drag_cube_root);
439
440 spawn_drag_cube(
441 &mut universe,
442 drag_cube_root,
443 [0.0, 0.0, 0.0],
444 [0.35, 0.85, 0.55, 1.0],
445 );
446 spawn_label(
447 &mut universe,
448 drag_cube_root,
449 [-0.25, -0.80, 0.0],
450 "drag\nto move",
451 );
452
453 // --- RIGHT: drag + click cube ---
454 let both_root = universe
455 .world
456 .add_component(TransformComponent::new().with_position(2.2, 0.0, 0.0));
457 let _ = universe.attach(scene, both_root);
458
459 spawn_both_cube(&mut universe, both_root, [0.0, 0.0, 0.0]);
460 spawn_label(
461 &mut universe,
462 both_root,
463 [-0.30, -0.80, 0.0],
464 "drag to move\nclick for color",
465 );
466
467 universe.systems.process_commands(
468 &mut universe.world,
469 &mut universe.visuals,
470 &mut universe.render_assets,
471 &mut universe.command_queue,
472 );
473
474 universe.enable_repl();
475 engine::Windowing::run_app(universe).expect("Windowing failed");
476}examples/text-animation.rs (line 20)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 let world = engine::ecs::World::default();
11 let mut universe = engine::Universe::new(world);
12
13 // Input-driven camera rig.
14 // Topology: I { T { C3D } }
15 let input = universe
16 .world
17 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
18 let camera3d = universe
19 .world
20 .add_component(engine::ecs::component::Camera3DComponent::new());
21 let rig_transform = universe.world.add_component(
22 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.5),
23 );
24 let input_mode = universe.world.add_component(
25 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
26 );
27 let _ = universe.attach(input, input_mode);
28 let _ = universe.attach(input, rig_transform);
29 let _ = universe.attach(rig_transform, camera3d);
30
31 // Small on-screen help.
32 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
33 universe.add(input);
34
35 // Light so we can see non-emissive materials.
36 let light_transform = universe.world.add_component(
37 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 2.0),
38 );
39 let light = universe.world.add_component(
40 engine::ecs::component::PointLightComponent::new()
41 .with_distance(25.0)
42 .with_color(1.0, 1.0, 1.0),
43 );
44 let _ = universe.attach(light_transform, light);
45 universe.add(light_transform);
46
47 use engine::ecs::component::{
48 ColorComponent, TextComponent, TextShadowComponent, TextureComponent,
49 TextureFilteringComponent, TransformComponent, TransparentCutoutComponent,
50 };
51
52 // Styled text root.
53 let text_root = universe.world.add_component(
54 TransformComponent::new()
55 .with_position(0.0, 0.0, 0.0)
56 .with_scale(0.25, 0.25, 1.0),
57 );
58
59 // Color must be an ancestor of the glyph renderables.
60 let color_id = universe
61 .world
62 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
63 let _ = universe.attach(text_root, color_id);
64
65 // This is the component we animate via SetText.
66 let text_id = universe.world.add_component(TextComponent::new(">w<"));
67 let _ = universe.attach(color_id, text_id);
68
69 // Route into cutout pass for cleaner edges.
70 let cutout = universe
71 .world
72 .add_component(TransparentCutoutComponent::new());
73 let _ = universe.attach(text_id, cutout);
74
75 // Font atlas.
76 let tex = universe.world.add_component(TextureComponent::with_uri(
77 "assets/textures/font_system.dds",
78 ));
79 let _ = universe.attach(text_id, tex);
80
81 let shadow = universe.world.add_component(
82 TextShadowComponent::new()
83 .with_scale(1.35)
84 .with_offset([0.06, -0.06, 0.0015]),
85 );
86 let filtering = universe
87 .world
88 .add_component(TextureFilteringComponent::nearest_magnification());
89
90 let _ = universe.attach(text_id, shadow);
91 let _ = universe.attach(text_id, filtering);
92
93 universe.add(text_root);
94
95 let clock_component = universe
96 .world
97 .add_component(engine::ecs::component::ClockComponent::new().with_bpm(140.0));
98 let _ = universe.add(clock_component);
99
100 // Looping animation: 4 beats long, 4 keyframes.
101 let anim = universe
102 .world
103 .add_component(engine::ecs::component::AnimationComponent::new());
104
105 let faces = [">w<", "^w^", "-_-", "o_o"];
106 for (i, &face) in faces.iter().enumerate() {
107 let kf = universe
108 .world
109 .add_component(engine::ecs::component::KeyframeComponent::new(i as f64));
110
111 // Each keyframe emits a SetText intent.
112 let action = universe
113 .world
114 .add_component(engine::ecs::component::ActionComponent::new(
115 engine::ecs::IntentValue::SetText {
116 component_ids: vec![text_id],
117 text: face.to_string(),
118 },
119 ));
120
121 let _ = universe.attach(anim, kf);
122 let _ = universe.attach(kf, action);
123 }
124
125 universe.add(anim);
126
127 // Process init-time registrations (Text expands into glyph subtrees here).
128 universe.systems.process_commands(
129 &mut universe.world,
130 &mut universe.visuals,
131 &mut universe.render_assets,
132 &mut universe.command_queue,
133 );
134
135 engine::Windowing::run_app(universe).expect("Windowing failed");
136}Additional examples can be found in:
- examples/button-press.rs
- examples/vtuber-example.rs
- examples/openxr.rs
- examples/transparent-cutout-example.rs
- examples/text-example.rs
- examples/gestures-and-gizmos.rs
- examples/background-example.rs
- examples/mesh-factory-example.rs
- examples/simple-demo.rs
- examples/background-occlusion-example.rs
- examples/raycast-topology-animation.rs
- examples/animation-for-topology.rs
- examples/vr-input.rs
- examples/font-example.rs
- examples/vtuber-joints-example.rs
- examples/animation-example.rs
- examples/folder-text.rs
- examples/collision-perimeter.rs
- examples/audio-graph-example.rs
- examples/gravity-fields.rs
Sourcepub fn with_fov(self, fov_y_degrees: f32) -> Self
pub fn with_fov(self, fov_y_degrees: f32) -> Self
Examples found in repository?
examples/audio-clip-instance-demo.rs (line 34)
13fn main() {
14 mittens_engine::example_support::ensure_model_assets();
15 utils::logger::init();
16
17 println!("[audio-clip-instance-demo] start");
18
19 let world = engine::ecs::World::default();
20 let mut universe = engine::Universe::new(world);
21
22 // Minimal camera so the window opens.
23 let input = universe
24 .world
25 .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
26 let rig = universe.world.add_component(
27 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 3.0),
28 );
29 let input_mode = universe.world.add_component(
30 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
31 );
32 let cam = universe
33 .world
34 .add_component(engine::ecs::component::Camera3DComponent::new().with_fov(60.0));
35 let _ = universe.attach(input, input_mode);
36 let _ = universe.attach(input, rig);
37 let _ = universe.attach(rig, cam);
38 universe.add(input);
39
40 let clear = universe
41 .world
42 .add_component(engine::ecs::component::BackgroundColorComponent::new());
43 let clear_c = universe
44 .world
45 .add_component(engine::ecs::component::ColorComponent::rgba(
46 0.06, 0.07, 0.10, 1.0,
47 ));
48 let _ = universe.world.add_child(clear, clear_c);
49 universe.add(clear);
50
51 let source = include_str!("audio-clip-instance-demo.mms");
52 let output = scripting::MeowMeowRunner::eval_with_world_at_path(
53 source,
54 Some("examples/audio-clip-instance-demo.mms"),
55 &mut universe.world,
56 &mut universe.systems.rx,
57 &mut universe.command_queue,
58 );
59
60 for error in &output.errors {
61 eprintln!("[mms] {error}");
62 }
63 println!(
64 "[audio-clip-instance-demo] mms ok: {} intent(s)",
65 output.intents.len()
66 );
67
68 for intent in output.intents {
69 universe
70 .command_queue
71 .push_intent_now(engine::ecs::ComponentId::default(), intent);
72 }
73
74 universe.systems.process_commands(
75 &mut universe.world,
76 &mut universe.visuals,
77 &mut universe.render_assets,
78 &mut universe.command_queue,
79 );
80
81 universe.enable_repl();
82 engine::Windowing::run_app(universe).expect("Windowing failed");
83}More examples
examples/audio-music-demo.rs (line 32)
11fn main() {
12 mittens_engine::example_support::ensure_model_assets();
13 utils::logger::init();
14
15 println!("[audio-music-demo] start");
16
17 let world = engine::ecs::World::default();
18 let mut universe = engine::Universe::new(world);
19
20 // Minimal camera so the window opens.
21 let input = universe
22 .world
23 .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
24 let rig = universe.world.add_component(
25 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 3.0),
26 );
27 let input_mode = universe.world.add_component(
28 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
29 );
30 let cam = universe
31 .world
32 .add_component(engine::ecs::component::Camera3DComponent::new().with_fov(60.0));
33 let _ = universe.attach(input, input_mode);
34 let _ = universe.attach(input, rig);
35 let _ = universe.attach(rig, cam);
36 universe.add(input);
37
38 // Dim clear color so console output stays readable in case of errors.
39 let clear = universe
40 .world
41 .add_component(engine::ecs::component::BackgroundColorComponent::new());
42 let clear_c = universe
43 .world
44 .add_component(engine::ecs::component::ColorComponent::rgba(
45 0.06, 0.07, 0.10, 1.0,
46 ));
47 let _ = universe.world.add_child(clear, clear_c);
48 universe.add(clear);
49
50 let source = include_str!("audio-music-demo.mms");
51 let output = scripting::MeowMeowRunner::eval_with_world_at_path(
52 source,
53 Some("examples/audio-music-demo.mms"),
54 &mut universe.world,
55 &mut universe.systems.rx,
56 &mut universe.command_queue,
57 );
58
59 for error in &output.errors {
60 eprintln!("[mms] {error}");
61 }
62 println!(
63 "[audio-music-demo] mms ok: {} intent(s)",
64 output.intents.len()
65 );
66
67 for intent in output.intents {
68 universe
69 .command_queue
70 .push_intent_now(engine::ecs::ComponentId::default(), intent);
71 }
72
73 universe.systems.process_commands(
74 &mut universe.world,
75 &mut universe.visuals,
76 &mut universe.render_assets,
77 &mut universe.command_queue,
78 );
79
80 universe.enable_repl();
81 engine::Windowing::run_app(universe).expect("Windowing failed");
82}examples/pointer-events.rs (line 403)
349fn main() {
350 mittens_engine::example_support::ensure_model_assets();
351 utils::logger::init();
352
353 let world = engine::ecs::World::default();
354 let mut universe = engine::Universe::new(world);
355
356 use engine::ecs::component::{
357 AmbientLightComponent, BackgroundColorComponent, Camera3DComponent, ColorComponent,
358 DirectionalLightComponent, InputComponent, InputTransformModeComponent, PointerComponent,
359 TransformComponent,
360 };
361
362 // Background.
363 let bg = universe
364 .world
365 .add_component(BackgroundColorComponent::new());
366 let bg_c = universe
367 .world
368 .add_component(ColorComponent::rgba(0.12, 0.12, 0.16, 1.0));
369 let _ = universe.world.add_child(bg, bg_c);
370 universe.add(bg);
371
372 // Lighting.
373 let ambient = universe
374 .world
375 .add_component(AmbientLightComponent::rgb(0.30, 0.30, 0.32));
376 universe.add(ambient);
377
378 let sun_t = universe
379 .world
380 .add_component(TransformComponent::new().with_position(2.0, 4.0, 3.0));
381 let sun = universe
382 .world
383 .add_component(DirectionalLightComponent::new());
384 let _ = universe.attach(sun_t, sun);
385 universe.add(sun_t);
386
387 // Camera + pointer rig.
388 let input = universe
389 .world
390 .add_component(InputComponent::new().with_speed(3.0));
391 let input_mode = universe.world.add_component(
392 InputTransformModeComponent::forward_z()
393 .with_fps_rotation()
394 .with_roll_axis_y(),
395 );
396 let _ = universe.attach(input, input_mode);
397
398 let cam_t = universe
399 .world
400 .add_component(TransformComponent::new().with_position(0.0, 0.5, 4.5));
401 let cam = universe
402 .world
403 .add_component(Camera3DComponent::new().with_fov(70.0));
404 let pointer = universe.world.add_component(PointerComponent::new());
405
406 let _ = universe.attach(input, cam_t);
407 let _ = universe.attach(cam_t, cam);
408 let _ = universe.attach(cam, pointer);
409
410 example_util::spawn_desktop_camera_controls_hint(&mut universe, cam_t);
411 universe.add(input);
412
413 // Scene root for all interactive objects.
414 let scene = universe.world.add_component(TransformComponent::new());
415 universe.add(scene);
416
417 // --- LEFT: click-only cubes ---
418 let click_group = universe
419 .world
420 .add_component(TransformComponent::new().with_position(-2.2, 0.0, 0.0));
421 let _ = universe.attach(scene, click_group);
422
423 for (i, dy) in [-0.55_f32, 0.0, 0.55].iter().enumerate() {
424 spawn_click_cube(&mut universe, click_group, [0.0, *dy, 0.0]);
425 let _ = i;
426 }
427 spawn_label(
428 &mut universe,
429 click_group,
430 [-0.25, -1.05, 0.0],
431 "click\ncycles color",
432 );
433
434 // --- MID: drag-only cube ---
435 let drag_cube_root = universe
436 .world
437 .add_component(TransformComponent::new().with_position(0.0, 0.0, 0.0));
438 let _ = universe.attach(scene, drag_cube_root);
439
440 spawn_drag_cube(
441 &mut universe,
442 drag_cube_root,
443 [0.0, 0.0, 0.0],
444 [0.35, 0.85, 0.55, 1.0],
445 );
446 spawn_label(
447 &mut universe,
448 drag_cube_root,
449 [-0.25, -0.80, 0.0],
450 "drag\nto move",
451 );
452
453 // --- RIGHT: drag + click cube ---
454 let both_root = universe
455 .world
456 .add_component(TransformComponent::new().with_position(2.2, 0.0, 0.0));
457 let _ = universe.attach(scene, both_root);
458
459 spawn_both_cube(&mut universe, both_root, [0.0, 0.0, 0.0]);
460 spawn_label(
461 &mut universe,
462 both_root,
463 [-0.30, -0.80, 0.0],
464 "drag to move\nclick for color",
465 );
466
467 universe.systems.process_commands(
468 &mut universe.world,
469 &mut universe.visuals,
470 &mut universe.render_assets,
471 &mut universe.command_queue,
472 );
473
474 universe.enable_repl();
475 engine::Windowing::run_app(universe).expect("Windowing failed");
476}examples/button-press.rs (line 495)
439fn main() {
440 mittens_engine::example_support::ensure_model_assets();
441 utils::logger::init();
442
443 let world = engine::ecs::World::default();
444 let mut universe = engine::Universe::new(world);
445
446 // Minimal lit scene + pointer raycaster.
447 use engine::ecs::component::{
448 AmbientLightComponent, BackgroundColorComponent, Camera3DComponent,
449 DirectionalLightComponent, InputComponent, InputTransformModeComponent, PointerComponent,
450 TransformComponent,
451 };
452
453 let bg = universe
454 .world
455 .add_component(BackgroundColorComponent::new());
456 let bg_c = universe
457 .world
458 .add_component(engine::ecs::component::ColorComponent::rgba(
459 0.92, 0.92, 0.96, 1.0,
460 ));
461 let _ = universe.world.add_child(bg, bg_c);
462 universe.add(bg);
463
464 let ambient = universe
465 .world
466 .add_component(AmbientLightComponent::rgb(0.35, 0.35, 0.35));
467 universe.add(ambient);
468
469 let sun_t = universe
470 .world
471 .add_component(TransformComponent::new().with_position(1.0, 1.0, 1.0));
472 let sun = universe
473 .world
474 .add_component(DirectionalLightComponent::new());
475 let _ = universe.attach(sun_t, sun);
476 universe.add(sun_t);
477
478 let input = universe
479 .world
480 .add_component(InputComponent::new().with_speed(2.5));
481 let input_mode = universe.world.add_component(
482 InputTransformModeComponent::forward_z()
483 .with_fps_rotation()
484 .with_roll_axis_y(),
485 );
486 let _ = universe.attach(input, input_mode);
487
488 let rig_t = universe
489 .world
490 .add_component(TransformComponent::new().with_position(0.0, 0.0, 2.5));
491 let _ = universe.attach(input, rig_t);
492
493 let cam = universe
494 .world
495 .add_component(Camera3DComponent::new().with_far(600.0).with_fov(70.0));
496 let _ = universe.attach(rig_t, cam);
497
498 let pointer = universe.world.add_component(PointerComponent::new());
499 let _ = universe.attach(cam, pointer);
500
501 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_t);
502 universe.add(input);
503
504 // The button.
505 let button_root = spawn_button(
506 &mut universe,
507 [0.0, 0.0, 0.0],
508 [1.20, 0.45],
509 ButtonBorder::new(0.025, 0.025, 0.025, 0.025),
510 "click me",
511 0.18,
512 [0.15, 0.15, 0.20, 1.0],
513 [0.85, 0.85, 0.92, 1.0],
514 );
515
516 // A small 3-cube stack to the left of the button for gizmo testing:
517 // [cyan]
518 // [yellow][light brown]
519 //
520 // These live under an EditorComponent subtree so clicking attaches the editor gizmo.
521 {
522 use engine::ecs::component::{EditorComponent, TransformComponent};
523
524 let editor_root = universe.world.add_component(EditorComponent::new());
525
526 // Position the stack in world space (left of the button).
527 let stack_root = universe
528 .world
529 .add_component(TransformComponent::new().with_position(-1.55, 0.0, 0.0));
530 let _ = universe.attach(editor_root, stack_root);
531
532 let cube = 0.22_f32;
533 let gap = 0.04_f32;
534 let step = cube + gap;
535 let y_bottom = -0.5 * step;
536 let y_top = 0.5 * step;
537 let x_left = -0.5 * step;
538 let x_right = 0.5 * step;
539
540 let yellow = [1.0, 0.92, 0.22, 1.0];
541 let light_brown = [0.80, 0.66, 0.46, 1.0];
542 let cyan = [0.20, 0.95, 1.0, 1.0];
543
544 let _bottom_left = spawn_raycastable_colored_cube(
545 &mut universe,
546 stack_root,
547 [x_left, y_bottom, 0.0],
548 [cube, cube, 0.06],
549 yellow,
550 );
551 let _bottom_right = spawn_raycastable_colored_cube(
552 &mut universe,
553 stack_root,
554 [x_right, y_bottom, 0.0],
555 [cube, cube, 0.06],
556 light_brown,
557 );
558 let _top = spawn_raycastable_colored_cube(
559 &mut universe,
560 stack_root,
561 [0.0, y_top, 0.0],
562 [cube, cube, 0.06],
563 cyan,
564 );
565
566 universe.add(editor_root);
567 }
568
569 universe.add_signal_handler(
570 engine::ecs::SignalKind::DragStart,
571 button_root,
572 button_press_handler,
573 );
574 universe.add_signal_handler(
575 engine::ecs::SignalKind::DragEnd,
576 button_root,
577 button_press_handler,
578 );
579
580 universe.systems.process_commands(
581 &mut universe.world,
582 &mut universe.visuals,
583 &mut universe.render_assets,
584 &mut universe.command_queue,
585 );
586
587 universe.enable_repl();
588 engine::Windowing::run_app(universe).expect("Windowing failed");
589}examples/transparent-cutout-example.rs (line 74)
34fn main() {
35 mittens_engine::example_support::ensure_model_assets();
36 utils::logger::init();
37
38 let world = engine::ecs::World::default();
39 let mut universe = engine::Universe::new(world);
40
41 // Orange/yellow-ish clear color so cutout edges read.
42 let clear = universe
43 .world
44 .add_component(BackgroundColorComponent::new());
45 let clear_c = universe
46 .world
47 .add_component(ColorComponent::rgba(0.98, 0.72, 0.22, 1.0));
48 let _ = universe.world.add_child(clear, clear_c);
49 universe.add(clear);
50
51 // Warm-ish ambient so the gold cubes don’t go too dark.
52 let ambient = universe
53 .world
54 .add_component(AmbientLightComponent::rgb(0.22, 0.16, 0.08));
55 universe.add(ambient);
56
57 // --- Camera rig (WASD/QE) ---
58 let input = universe
59 .world
60 .add_component(InputComponent::new().with_speed(2.0));
61 let input_mode = universe
62 .world
63 .add_component(InputTransformModeComponent::forward_z().with_roll_axis_y());
64 let _ = universe.attach(input, input_mode);
65
66 // Start a bit pulled back, looking toward the origin.
67 let rig_transform = universe
68 .world
69 .add_component(TransformComponent::new().with_position(0.0, 0.0, 5.0));
70 let _ = universe.attach(input, rig_transform);
71
72 let camera3d = universe
73 .world
74 .add_component(Camera3DComponent::new().with_far(200.0).with_fov(55.0));
75 let _ = universe.attach(rig_transform, camera3d);
76
77 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
78 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
79 universe.add(input);
80
81 // Key light for toon shading.
82 let light = universe.world.add_component(
83 PointLightComponent::new()
84 .with_distance(50.0)
85 .with_intensity(2.2)
86 .with_color(1.0, 0.98, 0.92),
87 );
88 let light_transform = universe
89 .world
90 .add_component(TransformComponent::new().with_position(2.0, 3.0, 4.0));
91 let _ = universe.attach(light_transform, light);
92 universe.add(light_transform);
93
94 // --- Transparent cutout: 100 cat faces in a 10x10 grid ---
95 // Place them *behind* the starting camera position (camera starts at z=+5.0).
96 // Not attached to the camera rig.
97 let cat_grid_root = universe
98 .world
99 .add_component(TransformComponent::new().with_position(0.0, 0.0, 9.0));
100 universe.add(cat_grid_root);
101
102 let grid_w: i32 = 10;
103 let grid_h: i32 = 10;
104 let spacing: f32 = 0.7;
105 let half_w = (grid_w as f32 - 1.0) * spacing * 0.5;
106 let half_h = (grid_h as f32 - 1.0) * spacing * 0.5;
107
108 for y in 0..grid_h {
109 for x in 0..grid_w {
110 // Topology: cat_grid_root { T_quad { R_quad { Texture + Filtering + Cutout + Color } } }
111 let px = x as f32 * spacing - half_w;
112 let py = y as f32 * spacing - half_h;
113
114 let pz: f32 = (x as f32) % half_w;
115
116 let quad_t = universe.world.add_component(
117 TransformComponent::new()
118 .with_position(px, py, pz)
119 .with_scale(0.55, 0.55, 1.0),
120 );
121 let quad_r = universe.world.add_component(RenderableComponent::square());
122
123 let quad_tex = universe.world.add_component(TextureComponent::with_uri(
124 "assets/textures/cat-face-amused.dds",
125 ));
126 let quad_filtering = universe
127 .world
128 .add_component(TextureFilteringComponent::linear());
129 let quad_cutout = universe
130 .world
131 .add_component(TransparentCutoutComponent::new());
132 let quad_color = universe
133 .world
134 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
135
136 let _ = universe.attach(cat_grid_root, quad_t);
137 let _ = universe.attach(quad_t, quad_r);
138 let _ = universe.attach(quad_r, quad_tex);
139 let _ = universe.attach(quad_r, quad_filtering);
140 let _ = universe.attach(quad_r, quad_cutout);
141 let _ = universe.attach(quad_r, quad_color);
142 }
143 }
144
145 // --- Gold/yellow cubes behind the quad ---
146 // Parent them under a transform so it’s easy to tweak their depth.
147 let cubes_root = universe
148 .world
149 .add_component(TransformComponent::new().with_position(0.0, 0.0, 4.6));
150
151 // point light for cats
152 let cat_light_tx = universe
153 .world
154 .add_component(TransformComponent::new().with_position(0.0, 2.0, 7.0));
155
156 let cat_light = universe.world.add_component(
157 PointLightComponent::new()
158 .with_distance(150.0)
159 .with_intensity(1.5)
160 .with_color(1.0, 0.98, 0.92),
161 );
162 let _ = universe.attach(cat_light_tx, cat_light);
163 let _ = universe.attach(cubes_root, cat_light_tx);
164
165 universe.add(cubes_root);
166
167 let gold_a = (1.0, 0.86, 0.22);
168 let gold_b = (1.0, 0.74, 0.10);
169 let gold_c = (0.95, 0.92, 0.32);
170
171 // A loose cluster that’s visible through the cutout (transparent) area.
172 spawn_gold_cube(&mut universe, cubes_root, (-1.1, -0.3, -0.2), 0.45, gold_a);
173 spawn_gold_cube(&mut universe, cubes_root, (1.0, -0.4, -0.4), 0.40, gold_b);
174 spawn_gold_cube(&mut universe, cubes_root, (-0.2, 0.9, -0.6), 0.38, gold_c);
175 spawn_gold_cube(&mut universe, cubes_root, (0.7, 0.6, -0.9), 0.32, gold_a);
176 spawn_gold_cube(&mut universe, cubes_root, (-0.8, 0.4, -1.1), 0.36, gold_b);
177
178 // Bigger orange/yellow cubes behind the cluster (to make the cutout depth obvious).
179 let orange_gold_a = (1.0, 0.62, 0.10);
180 let orange_gold_b = (1.0, 0.78, 0.18);
181 spawn_gold_cube(
182 &mut universe,
183 cubes_root,
184 (0.0, -0.1, -2.1),
185 1.15,
186 orange_gold_b,
187 );
188 spawn_gold_cube(
189 &mut universe,
190 cubes_root,
191 (-1.8, 0.6, -2.6),
192 0.95,
193 orange_gold_a,
194 );
195 spawn_gold_cube(
196 &mut universe,
197 cubes_root,
198 (1.9, 0.7, -2.9),
199 1.05,
200 orange_gold_b,
201 );
202 spawn_gold_cube(
203 &mut universe,
204 cubes_root,
205 (0.9, 1.8, -3.2),
206 0.90,
207 orange_gold_a,
208 );
209 spawn_gold_cube(
210 &mut universe,
211 cubes_root,
212 (-0.9, 1.7, -3.5),
213 1.10,
214 orange_gold_b,
215 );
216
217 // Process init-time registrations (loads textures, registers renderables, etc.).
218 universe.systems.process_commands(
219 &mut universe.world,
220 &mut universe.visuals,
221 &mut universe.render_assets,
222 &mut universe.command_queue,
223 );
224
225 universe.enable_repl();
226 engine::Windowing::run_app(universe).expect("Windowing failed");
227}examples/gestures-and-gizmos.rs (line 113)
13fn build_gestures_and_gizmos_scene(universe: &mut engine::Universe) -> Scene {
14 use engine::ecs::component::{
15 BackgroundColorComponent, BackgroundComponent, Camera3DComponent, ColorComponent,
16 DirectionalLightComponent, InputComponent, InputTransformModeComponent, PointerComponent,
17 RaycastableComponent, RenderableComponent, TransformComponent, TransformGizmoComponent,
18 };
19 use engine::graphics::BuiltinMeshType;
20 use engine::graphics::primitives::{MaterialHandle, Renderable};
21
22 let tri_mesh = universe.render_assets.get_mesh(BuiltinMeshType::Triangle2D);
23 let cube_mesh = universe.render_assets.get_mesh(BuiltinMeshType::Cube);
24 let tetra_mesh = universe
25 .render_assets
26 .get_mesh(BuiltinMeshType::Tetrahedron);
27
28 // BackgroundColor { C.rgba }
29 let bg_color = universe
30 .world
31 .add_component(BackgroundColorComponent::new());
32 let bg_color_c = universe
33 .world
34 .add_component(ColorComponent::rgba(0.90, 0.90, 0.90, 1.0));
35 let _ = universe.world.add_child(bg_color, bg_color_c);
36 universe.add(bg_color);
37
38 // ambient light
39 let ambient = universe
40 .world
41 .add_component(AmbientLightComponent::rgb(0.25, 0.25, 0.25));
42 universe.add(ambient);
43
44 // ground plane
45 let ground_tx = universe.world.add_component(
46 TransformComponent::new()
47 .with_position(0.0, -2.5, 0.0)
48 .with_scale(20.0, 1.0, 20.0),
49 );
50 let ground_r = universe
51 .world
52 .add_component(RenderableComponent::new(Renderable::new(
53 universe.render_assets.get_mesh(BuiltinMeshType::Cube),
54 MaterialHandle::TOON_MESH,
55 )));
56 let ground_c = universe
57 .world
58 .add_component(ColorComponent::rgba(0.75, 0.75, 0.75, 1.0));
59 let _ = universe.attach(ground_tx, ground_r);
60 let _ = universe.attach(ground_r, ground_c);
61 let _ = universe.add(ground_tx);
62
63 // Background {
64 // with_occlusion_and_lighting()
65 // // using the example utils to add clouds to the background
66 // }
67 let bg_root = universe
68 .world
69 .add_component(BackgroundComponent::new().with_occlusion_and_lighting());
70 universe.add(bg_root);
71
72 // DirectionalLight {
73 // T { translate [1, 1, 1] }
74 // }
75 // Directional lights encode their direction in the node's world position.
76 let sun_t = universe
77 .world
78 .add_component(TransformComponent::new().with_position(1.0, 1.0, 1.0));
79 let sun = universe
80 .world
81 .add_component(DirectionalLightComponent::new());
82 let _ = universe.attach(sun_t, sun);
83 universe.add(sun_t);
84
85 // i = input
86 // t = transform
87 // c3d = camera3d
88 //
89 // I {
90 // T {
91 // C3D { with_fps_rotation().with_roll_axis_y() }
92 // }
93 // }
94 let input = universe
95 .world
96 .add_component(InputComponent::new().with_speed(2.5));
97 let input_mode = universe.world.add_component(
98 InputTransformModeComponent::forward_z()
99 .with_fps_rotation()
100 .with_roll_axis_y(),
101 );
102
103 let _ = universe.attach(input, input_mode);
104
105 // Forward is -Z, so put the camera at +Z looking toward the origin.
106 let rig_t = universe
107 .world
108 .add_component(TransformComponent::new().with_position(0.0, 0.0, 3.5));
109 let _ = universe.attach(input, rig_t);
110
111 let cam = universe
112 .world
113 .add_component(Camera3DComponent::new().with_far(600.0).with_fov(70.0));
114 let _ = universe.attach(rig_t, cam);
115
116 // Opt-in: treat this camera rig as a pointer source.
117 let pointer = universe.world.add_component(PointerComponent::new());
118 let _ = universe.attach(cam, pointer);
119
120 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
121 example_util::spawn_desktop_camera_controls_hint(universe, rig_t);
122
123 fn spawn_shape_with_gizmo(
124 universe: &mut engine::Universe,
125 mesh: engine::graphics::primitives::CpuMeshHandle,
126 pos: [f32; 3],
127 scale: [f32; 3],
128 rot_euler: [f32; 3],
129 color: [f32; 4],
130 ) {
131 let t = universe.world.add_component(
132 TransformComponent::new()
133 .with_position(pos[0], pos[1], pos[2])
134 .with_scale(scale[0], scale[1], scale[2])
135 .with_rotation_euler(rot_euler[0], rot_euler[1], rot_euler[2]),
136 );
137 let r = universe
138 .world
139 .add_component(RenderableComponent::new(Renderable::new(
140 mesh,
141 MaterialHandle::TOON_MESH,
142 )));
143 let c = universe
144 .world
145 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
146 let rc = universe
147 .world
148 .add_component(RaycastableComponent::enabled());
149 let g = universe.world.add_component(TransformGizmoComponent::new());
150
151 let _ = universe.attach(t, r);
152 let _ = universe.attach(r, c);
153 let _ = universe.attach(r, rc);
154 let _ = universe.attach(t, g);
155
156 universe.add(t);
157 }
158
159 fn spawn_shape_raycastable_no_gizmo(
160 universe: &mut engine::Universe,
161 mesh: engine::graphics::primitives::CpuMeshHandle,
162 pos: [f32; 3],
163 scale: [f32; 3],
164 rot_euler: [f32; 3],
165 color: [f32; 4],
166 ) {
167 let t = universe.world.add_component(
168 TransformComponent::new()
169 .with_position(pos[0], pos[1], pos[2])
170 .with_scale(scale[0], scale[1], scale[2])
171 .with_rotation_euler(rot_euler[0], rot_euler[1], rot_euler[2]),
172 );
173 let r = universe
174 .world
175 .add_component(RenderableComponent::new(Renderable::new(
176 mesh,
177 MaterialHandle::TOON_MESH,
178 )));
179 let c = universe
180 .world
181 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
182 let rc = universe
183 .world
184 .add_component(RaycastableComponent::enabled());
185
186 let _ = universe.attach(t, r);
187 let _ = universe.attach(r, c);
188 let _ = universe.attach(r, rc);
189
190 universe.add(t);
191 }
192
193 spawn_shape_with_gizmo(
194 universe,
195 tri_mesh,
196 [-1.2, 0.0, 0.0],
197 [0.65, 0.65, 0.65],
198 [0.0, 0.0, 0.0],
199 [0.2, 0.9, 0.25, 1.0],
200 );
201 spawn_shape_with_gizmo(
202 universe,
203 cube_mesh,
204 [0.0, 0.0, 0.0],
205 [0.55, 0.55, 0.55],
206 [0.0, 0.0, 0.0],
207 [0.95, 0.25, 0.2, 1.0],
208 );
209 spawn_shape_with_gizmo(
210 universe,
211 tetra_mesh,
212 [1.2, 0.0, 0.0],
213 [0.7, 0.7, 0.7],
214 [0.0, 0.0, 0.0],
215 [0.2, 0.55, 1.0, 1.0],
216 );
217
218 // Standalone tetrahedron: no gizmo, but explicitly raycastable.
219 // This helps isolate tetra picking vs gizmo-handle interception.
220 spawn_shape_raycastable_no_gizmo(
221 universe,
222 tetra_mesh,
223 [2.6, -0.6, 0.0],
224 [0.95, 0.95, 0.95],
225 [0.0, 0.0, 0.0],
226 [0.85, 0.85, 1.0, 1.0],
227 );
228
229 universe.add(input);
230
231 Scene { bg_root }
232}pub fn with_near(self, z_near: f32) -> Self
Sourcepub fn with_far(self, z_far: f32) -> Self
pub fn with_far(self, z_far: f32) -> Self
Examples found in repository?
examples/button-press.rs (line 495)
439fn main() {
440 mittens_engine::example_support::ensure_model_assets();
441 utils::logger::init();
442
443 let world = engine::ecs::World::default();
444 let mut universe = engine::Universe::new(world);
445
446 // Minimal lit scene + pointer raycaster.
447 use engine::ecs::component::{
448 AmbientLightComponent, BackgroundColorComponent, Camera3DComponent,
449 DirectionalLightComponent, InputComponent, InputTransformModeComponent, PointerComponent,
450 TransformComponent,
451 };
452
453 let bg = universe
454 .world
455 .add_component(BackgroundColorComponent::new());
456 let bg_c = universe
457 .world
458 .add_component(engine::ecs::component::ColorComponent::rgba(
459 0.92, 0.92, 0.96, 1.0,
460 ));
461 let _ = universe.world.add_child(bg, bg_c);
462 universe.add(bg);
463
464 let ambient = universe
465 .world
466 .add_component(AmbientLightComponent::rgb(0.35, 0.35, 0.35));
467 universe.add(ambient);
468
469 let sun_t = universe
470 .world
471 .add_component(TransformComponent::new().with_position(1.0, 1.0, 1.0));
472 let sun = universe
473 .world
474 .add_component(DirectionalLightComponent::new());
475 let _ = universe.attach(sun_t, sun);
476 universe.add(sun_t);
477
478 let input = universe
479 .world
480 .add_component(InputComponent::new().with_speed(2.5));
481 let input_mode = universe.world.add_component(
482 InputTransformModeComponent::forward_z()
483 .with_fps_rotation()
484 .with_roll_axis_y(),
485 );
486 let _ = universe.attach(input, input_mode);
487
488 let rig_t = universe
489 .world
490 .add_component(TransformComponent::new().with_position(0.0, 0.0, 2.5));
491 let _ = universe.attach(input, rig_t);
492
493 let cam = universe
494 .world
495 .add_component(Camera3DComponent::new().with_far(600.0).with_fov(70.0));
496 let _ = universe.attach(rig_t, cam);
497
498 let pointer = universe.world.add_component(PointerComponent::new());
499 let _ = universe.attach(cam, pointer);
500
501 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_t);
502 universe.add(input);
503
504 // The button.
505 let button_root = spawn_button(
506 &mut universe,
507 [0.0, 0.0, 0.0],
508 [1.20, 0.45],
509 ButtonBorder::new(0.025, 0.025, 0.025, 0.025),
510 "click me",
511 0.18,
512 [0.15, 0.15, 0.20, 1.0],
513 [0.85, 0.85, 0.92, 1.0],
514 );
515
516 // A small 3-cube stack to the left of the button for gizmo testing:
517 // [cyan]
518 // [yellow][light brown]
519 //
520 // These live under an EditorComponent subtree so clicking attaches the editor gizmo.
521 {
522 use engine::ecs::component::{EditorComponent, TransformComponent};
523
524 let editor_root = universe.world.add_component(EditorComponent::new());
525
526 // Position the stack in world space (left of the button).
527 let stack_root = universe
528 .world
529 .add_component(TransformComponent::new().with_position(-1.55, 0.0, 0.0));
530 let _ = universe.attach(editor_root, stack_root);
531
532 let cube = 0.22_f32;
533 let gap = 0.04_f32;
534 let step = cube + gap;
535 let y_bottom = -0.5 * step;
536 let y_top = 0.5 * step;
537 let x_left = -0.5 * step;
538 let x_right = 0.5 * step;
539
540 let yellow = [1.0, 0.92, 0.22, 1.0];
541 let light_brown = [0.80, 0.66, 0.46, 1.0];
542 let cyan = [0.20, 0.95, 1.0, 1.0];
543
544 let _bottom_left = spawn_raycastable_colored_cube(
545 &mut universe,
546 stack_root,
547 [x_left, y_bottom, 0.0],
548 [cube, cube, 0.06],
549 yellow,
550 );
551 let _bottom_right = spawn_raycastable_colored_cube(
552 &mut universe,
553 stack_root,
554 [x_right, y_bottom, 0.0],
555 [cube, cube, 0.06],
556 light_brown,
557 );
558 let _top = spawn_raycastable_colored_cube(
559 &mut universe,
560 stack_root,
561 [0.0, y_top, 0.0],
562 [cube, cube, 0.06],
563 cyan,
564 );
565
566 universe.add(editor_root);
567 }
568
569 universe.add_signal_handler(
570 engine::ecs::SignalKind::DragStart,
571 button_root,
572 button_press_handler,
573 );
574 universe.add_signal_handler(
575 engine::ecs::SignalKind::DragEnd,
576 button_root,
577 button_press_handler,
578 );
579
580 universe.systems.process_commands(
581 &mut universe.world,
582 &mut universe.visuals,
583 &mut universe.render_assets,
584 &mut universe.command_queue,
585 );
586
587 universe.enable_repl();
588 engine::Windowing::run_app(universe).expect("Windowing failed");
589}More examples
examples/transparent-cutout-example.rs (line 74)
34fn main() {
35 mittens_engine::example_support::ensure_model_assets();
36 utils::logger::init();
37
38 let world = engine::ecs::World::default();
39 let mut universe = engine::Universe::new(world);
40
41 // Orange/yellow-ish clear color so cutout edges read.
42 let clear = universe
43 .world
44 .add_component(BackgroundColorComponent::new());
45 let clear_c = universe
46 .world
47 .add_component(ColorComponent::rgba(0.98, 0.72, 0.22, 1.0));
48 let _ = universe.world.add_child(clear, clear_c);
49 universe.add(clear);
50
51 // Warm-ish ambient so the gold cubes don’t go too dark.
52 let ambient = universe
53 .world
54 .add_component(AmbientLightComponent::rgb(0.22, 0.16, 0.08));
55 universe.add(ambient);
56
57 // --- Camera rig (WASD/QE) ---
58 let input = universe
59 .world
60 .add_component(InputComponent::new().with_speed(2.0));
61 let input_mode = universe
62 .world
63 .add_component(InputTransformModeComponent::forward_z().with_roll_axis_y());
64 let _ = universe.attach(input, input_mode);
65
66 // Start a bit pulled back, looking toward the origin.
67 let rig_transform = universe
68 .world
69 .add_component(TransformComponent::new().with_position(0.0, 0.0, 5.0));
70 let _ = universe.attach(input, rig_transform);
71
72 let camera3d = universe
73 .world
74 .add_component(Camera3DComponent::new().with_far(200.0).with_fov(55.0));
75 let _ = universe.attach(rig_transform, camera3d);
76
77 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
78 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
79 universe.add(input);
80
81 // Key light for toon shading.
82 let light = universe.world.add_component(
83 PointLightComponent::new()
84 .with_distance(50.0)
85 .with_intensity(2.2)
86 .with_color(1.0, 0.98, 0.92),
87 );
88 let light_transform = universe
89 .world
90 .add_component(TransformComponent::new().with_position(2.0, 3.0, 4.0));
91 let _ = universe.attach(light_transform, light);
92 universe.add(light_transform);
93
94 // --- Transparent cutout: 100 cat faces in a 10x10 grid ---
95 // Place them *behind* the starting camera position (camera starts at z=+5.0).
96 // Not attached to the camera rig.
97 let cat_grid_root = universe
98 .world
99 .add_component(TransformComponent::new().with_position(0.0, 0.0, 9.0));
100 universe.add(cat_grid_root);
101
102 let grid_w: i32 = 10;
103 let grid_h: i32 = 10;
104 let spacing: f32 = 0.7;
105 let half_w = (grid_w as f32 - 1.0) * spacing * 0.5;
106 let half_h = (grid_h as f32 - 1.0) * spacing * 0.5;
107
108 for y in 0..grid_h {
109 for x in 0..grid_w {
110 // Topology: cat_grid_root { T_quad { R_quad { Texture + Filtering + Cutout + Color } } }
111 let px = x as f32 * spacing - half_w;
112 let py = y as f32 * spacing - half_h;
113
114 let pz: f32 = (x as f32) % half_w;
115
116 let quad_t = universe.world.add_component(
117 TransformComponent::new()
118 .with_position(px, py, pz)
119 .with_scale(0.55, 0.55, 1.0),
120 );
121 let quad_r = universe.world.add_component(RenderableComponent::square());
122
123 let quad_tex = universe.world.add_component(TextureComponent::with_uri(
124 "assets/textures/cat-face-amused.dds",
125 ));
126 let quad_filtering = universe
127 .world
128 .add_component(TextureFilteringComponent::linear());
129 let quad_cutout = universe
130 .world
131 .add_component(TransparentCutoutComponent::new());
132 let quad_color = universe
133 .world
134 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
135
136 let _ = universe.attach(cat_grid_root, quad_t);
137 let _ = universe.attach(quad_t, quad_r);
138 let _ = universe.attach(quad_r, quad_tex);
139 let _ = universe.attach(quad_r, quad_filtering);
140 let _ = universe.attach(quad_r, quad_cutout);
141 let _ = universe.attach(quad_r, quad_color);
142 }
143 }
144
145 // --- Gold/yellow cubes behind the quad ---
146 // Parent them under a transform so it’s easy to tweak their depth.
147 let cubes_root = universe
148 .world
149 .add_component(TransformComponent::new().with_position(0.0, 0.0, 4.6));
150
151 // point light for cats
152 let cat_light_tx = universe
153 .world
154 .add_component(TransformComponent::new().with_position(0.0, 2.0, 7.0));
155
156 let cat_light = universe.world.add_component(
157 PointLightComponent::new()
158 .with_distance(150.0)
159 .with_intensity(1.5)
160 .with_color(1.0, 0.98, 0.92),
161 );
162 let _ = universe.attach(cat_light_tx, cat_light);
163 let _ = universe.attach(cubes_root, cat_light_tx);
164
165 universe.add(cubes_root);
166
167 let gold_a = (1.0, 0.86, 0.22);
168 let gold_b = (1.0, 0.74, 0.10);
169 let gold_c = (0.95, 0.92, 0.32);
170
171 // A loose cluster that’s visible through the cutout (transparent) area.
172 spawn_gold_cube(&mut universe, cubes_root, (-1.1, -0.3, -0.2), 0.45, gold_a);
173 spawn_gold_cube(&mut universe, cubes_root, (1.0, -0.4, -0.4), 0.40, gold_b);
174 spawn_gold_cube(&mut universe, cubes_root, (-0.2, 0.9, -0.6), 0.38, gold_c);
175 spawn_gold_cube(&mut universe, cubes_root, (0.7, 0.6, -0.9), 0.32, gold_a);
176 spawn_gold_cube(&mut universe, cubes_root, (-0.8, 0.4, -1.1), 0.36, gold_b);
177
178 // Bigger orange/yellow cubes behind the cluster (to make the cutout depth obvious).
179 let orange_gold_a = (1.0, 0.62, 0.10);
180 let orange_gold_b = (1.0, 0.78, 0.18);
181 spawn_gold_cube(
182 &mut universe,
183 cubes_root,
184 (0.0, -0.1, -2.1),
185 1.15,
186 orange_gold_b,
187 );
188 spawn_gold_cube(
189 &mut universe,
190 cubes_root,
191 (-1.8, 0.6, -2.6),
192 0.95,
193 orange_gold_a,
194 );
195 spawn_gold_cube(
196 &mut universe,
197 cubes_root,
198 (1.9, 0.7, -2.9),
199 1.05,
200 orange_gold_b,
201 );
202 spawn_gold_cube(
203 &mut universe,
204 cubes_root,
205 (0.9, 1.8, -3.2),
206 0.90,
207 orange_gold_a,
208 );
209 spawn_gold_cube(
210 &mut universe,
211 cubes_root,
212 (-0.9, 1.7, -3.5),
213 1.10,
214 orange_gold_b,
215 );
216
217 // Process init-time registrations (loads textures, registers renderables, etc.).
218 universe.systems.process_commands(
219 &mut universe.world,
220 &mut universe.visuals,
221 &mut universe.render_assets,
222 &mut universe.command_queue,
223 );
224
225 universe.enable_repl();
226 engine::Windowing::run_app(universe).expect("Windowing failed");
227}examples/gestures-and-gizmos.rs (line 113)
13fn build_gestures_and_gizmos_scene(universe: &mut engine::Universe) -> Scene {
14 use engine::ecs::component::{
15 BackgroundColorComponent, BackgroundComponent, Camera3DComponent, ColorComponent,
16 DirectionalLightComponent, InputComponent, InputTransformModeComponent, PointerComponent,
17 RaycastableComponent, RenderableComponent, TransformComponent, TransformGizmoComponent,
18 };
19 use engine::graphics::BuiltinMeshType;
20 use engine::graphics::primitives::{MaterialHandle, Renderable};
21
22 let tri_mesh = universe.render_assets.get_mesh(BuiltinMeshType::Triangle2D);
23 let cube_mesh = universe.render_assets.get_mesh(BuiltinMeshType::Cube);
24 let tetra_mesh = universe
25 .render_assets
26 .get_mesh(BuiltinMeshType::Tetrahedron);
27
28 // BackgroundColor { C.rgba }
29 let bg_color = universe
30 .world
31 .add_component(BackgroundColorComponent::new());
32 let bg_color_c = universe
33 .world
34 .add_component(ColorComponent::rgba(0.90, 0.90, 0.90, 1.0));
35 let _ = universe.world.add_child(bg_color, bg_color_c);
36 universe.add(bg_color);
37
38 // ambient light
39 let ambient = universe
40 .world
41 .add_component(AmbientLightComponent::rgb(0.25, 0.25, 0.25));
42 universe.add(ambient);
43
44 // ground plane
45 let ground_tx = universe.world.add_component(
46 TransformComponent::new()
47 .with_position(0.0, -2.5, 0.0)
48 .with_scale(20.0, 1.0, 20.0),
49 );
50 let ground_r = universe
51 .world
52 .add_component(RenderableComponent::new(Renderable::new(
53 universe.render_assets.get_mesh(BuiltinMeshType::Cube),
54 MaterialHandle::TOON_MESH,
55 )));
56 let ground_c = universe
57 .world
58 .add_component(ColorComponent::rgba(0.75, 0.75, 0.75, 1.0));
59 let _ = universe.attach(ground_tx, ground_r);
60 let _ = universe.attach(ground_r, ground_c);
61 let _ = universe.add(ground_tx);
62
63 // Background {
64 // with_occlusion_and_lighting()
65 // // using the example utils to add clouds to the background
66 // }
67 let bg_root = universe
68 .world
69 .add_component(BackgroundComponent::new().with_occlusion_and_lighting());
70 universe.add(bg_root);
71
72 // DirectionalLight {
73 // T { translate [1, 1, 1] }
74 // }
75 // Directional lights encode their direction in the node's world position.
76 let sun_t = universe
77 .world
78 .add_component(TransformComponent::new().with_position(1.0, 1.0, 1.0));
79 let sun = universe
80 .world
81 .add_component(DirectionalLightComponent::new());
82 let _ = universe.attach(sun_t, sun);
83 universe.add(sun_t);
84
85 // i = input
86 // t = transform
87 // c3d = camera3d
88 //
89 // I {
90 // T {
91 // C3D { with_fps_rotation().with_roll_axis_y() }
92 // }
93 // }
94 let input = universe
95 .world
96 .add_component(InputComponent::new().with_speed(2.5));
97 let input_mode = universe.world.add_component(
98 InputTransformModeComponent::forward_z()
99 .with_fps_rotation()
100 .with_roll_axis_y(),
101 );
102
103 let _ = universe.attach(input, input_mode);
104
105 // Forward is -Z, so put the camera at +Z looking toward the origin.
106 let rig_t = universe
107 .world
108 .add_component(TransformComponent::new().with_position(0.0, 0.0, 3.5));
109 let _ = universe.attach(input, rig_t);
110
111 let cam = universe
112 .world
113 .add_component(Camera3DComponent::new().with_far(600.0).with_fov(70.0));
114 let _ = universe.attach(rig_t, cam);
115
116 // Opt-in: treat this camera rig as a pointer source.
117 let pointer = universe.world.add_component(PointerComponent::new());
118 let _ = universe.attach(cam, pointer);
119
120 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
121 example_util::spawn_desktop_camera_controls_hint(universe, rig_t);
122
123 fn spawn_shape_with_gizmo(
124 universe: &mut engine::Universe,
125 mesh: engine::graphics::primitives::CpuMeshHandle,
126 pos: [f32; 3],
127 scale: [f32; 3],
128 rot_euler: [f32; 3],
129 color: [f32; 4],
130 ) {
131 let t = universe.world.add_component(
132 TransformComponent::new()
133 .with_position(pos[0], pos[1], pos[2])
134 .with_scale(scale[0], scale[1], scale[2])
135 .with_rotation_euler(rot_euler[0], rot_euler[1], rot_euler[2]),
136 );
137 let r = universe
138 .world
139 .add_component(RenderableComponent::new(Renderable::new(
140 mesh,
141 MaterialHandle::TOON_MESH,
142 )));
143 let c = universe
144 .world
145 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
146 let rc = universe
147 .world
148 .add_component(RaycastableComponent::enabled());
149 let g = universe.world.add_component(TransformGizmoComponent::new());
150
151 let _ = universe.attach(t, r);
152 let _ = universe.attach(r, c);
153 let _ = universe.attach(r, rc);
154 let _ = universe.attach(t, g);
155
156 universe.add(t);
157 }
158
159 fn spawn_shape_raycastable_no_gizmo(
160 universe: &mut engine::Universe,
161 mesh: engine::graphics::primitives::CpuMeshHandle,
162 pos: [f32; 3],
163 scale: [f32; 3],
164 rot_euler: [f32; 3],
165 color: [f32; 4],
166 ) {
167 let t = universe.world.add_component(
168 TransformComponent::new()
169 .with_position(pos[0], pos[1], pos[2])
170 .with_scale(scale[0], scale[1], scale[2])
171 .with_rotation_euler(rot_euler[0], rot_euler[1], rot_euler[2]),
172 );
173 let r = universe
174 .world
175 .add_component(RenderableComponent::new(Renderable::new(
176 mesh,
177 MaterialHandle::TOON_MESH,
178 )));
179 let c = universe
180 .world
181 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
182 let rc = universe
183 .world
184 .add_component(RaycastableComponent::enabled());
185
186 let _ = universe.attach(t, r);
187 let _ = universe.attach(r, c);
188 let _ = universe.attach(r, rc);
189
190 universe.add(t);
191 }
192
193 spawn_shape_with_gizmo(
194 universe,
195 tri_mesh,
196 [-1.2, 0.0, 0.0],
197 [0.65, 0.65, 0.65],
198 [0.0, 0.0, 0.0],
199 [0.2, 0.9, 0.25, 1.0],
200 );
201 spawn_shape_with_gizmo(
202 universe,
203 cube_mesh,
204 [0.0, 0.0, 0.0],
205 [0.55, 0.55, 0.55],
206 [0.0, 0.0, 0.0],
207 [0.95, 0.25, 0.2, 1.0],
208 );
209 spawn_shape_with_gizmo(
210 universe,
211 tetra_mesh,
212 [1.2, 0.0, 0.0],
213 [0.7, 0.7, 0.7],
214 [0.0, 0.0, 0.0],
215 [0.2, 0.55, 1.0, 1.0],
216 );
217
218 // Standalone tetrahedron: no gizmo, but explicitly raycastable.
219 // This helps isolate tetra picking vs gizmo-handle interception.
220 spawn_shape_raycastable_no_gizmo(
221 universe,
222 tetra_mesh,
223 [2.6, -0.6, 0.0],
224 [0.95, 0.95, 0.95],
225 [0.0, 0.0, 0.0],
226 [0.85, 0.85, 1.0, 1.0],
227 );
228
229 universe.add(input);
230
231 Scene { bg_root }
232}examples/background-occlusion-example.rs (line 62)
19fn main() {
20 mittens_engine::example_support::ensure_model_assets();
21 utils::logger::init();
22
23 let world = engine::ecs::World::default();
24 let mut universe = engine::Universe::new(world);
25
26 // Light purple-red background so the occlusion reads.
27 let clear = universe
28 .world
29 .add_component(engine::ecs::component::BackgroundColorComponent::new());
30 let clear_c = universe
31 .world
32 .add_component(engine::ecs::component::ColorComponent::rgba(
33 0.62, 0.38, 0.56, 1.0,
34 ));
35 let _ = universe.world.add_child(clear, clear_c);
36 universe.add(clear);
37
38 // A bit of ambient so the cluster volume reads.
39 let ambient = universe
40 .world
41 .add_component(engine::ecs::component::AmbientLightComponent::rgb(
42 0.20, 0.15, 0.3,
43 ));
44 universe.add(ambient);
45
46 // --- Camera rig (WASD/QE) ---
47 let input = universe
48 .world
49 .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
50 let input_mode = universe.world.add_component(
51 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
52 );
53 let _ = universe.attach(input, input_mode);
54
55 let rig_transform = universe.world.add_component(
56 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 6.0),
57 );
58 let _ = universe.attach(input, rig_transform);
59
60 let camera3d = universe.world.add_component(
61 engine::ecs::component::Camera3DComponent::new()
62 .with_far(200.0)
63 .with_fov(70.0),
64 );
65 let _ = universe.attach(rig_transform, camera3d);
66
67 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
68 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
69
70 // Key directional light toward the cloud volume.
71 //
72 // Directional lights encode their direction in the node's world position.
73 // The shader normalizes this vector.
74 let sun = universe.world.add_component(
75 engine::ecs::component::DirectionalLightComponent::new()
76 .with_intensity(1.6)
77 .with_color(1.0, 0.98, 0.92),
78 );
79 // Pointing from the clouds back toward the camera a bit (+Z), and slightly from above.
80 let sun_dir = universe.world.add_component(
81 engine::ecs::component::TransformComponent::new().with_position(0.15, 0.65, 0.75),
82 );
83 let _ = universe.attach(sun_dir, sun);
84
85 // A couple point lights to fill/shade the cloud volume.
86 let light_a = universe.world.add_component(
87 engine::ecs::component::PointLightComponent::new()
88 .with_distance(80.0)
89 .with_intensity(3.0)
90 .with_color(0.9, 0.95, 1.0),
91 );
92 let light_a_tx = universe.world.add_component(
93 engine::ecs::component::TransformComponent::new().with_position(8.0, 8.0, 5.0),
94 );
95 let _ = universe.attach(light_a_tx, light_a);
96
97 let light_b = universe.world.add_component(
98 engine::ecs::component::PointLightComponent::new()
99 .with_distance(80.0)
100 .with_intensity(2.2)
101 .with_color(1.0, 0.9, 0.85),
102 );
103 let light_b_tx = universe.world.add_component(
104 engine::ecs::component::TransformComponent::new().with_position(-10.0, 4.0, -6.0),
105 );
106 let _ = universe.attach(light_b_tx, light_b);
107
108 universe.add(input);
109 universe.add(sun_dir);
110 universe.add(light_a_tx);
111 universe.add(light_b_tx);
112
113 let cube_mesh = universe
114 .render_assets
115 .get_mesh(engine::graphics::BuiltinMeshType::Cube);
116
117 // --- Background occluded+lit world ---
118 // Renderables under this node participate in a background stage that depth-writes for
119 // self-occlusion + uses the normal lighting shader inputs.
120 let bg_root = universe.world.add_component(
121 engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
122 );
123 universe.add(bg_root);
124
125 // Create overlapping cube clusters like cloud puffs.
126 // Place them generally in front of the camera (negative Z).
127 // Spawn two groups on opposite X sides.
128 let cluster_count: u32 = 9;
129 for (group_i, group_x) in [-16.0_f32, 16.0_f32].into_iter().enumerate() {
130 let group_tx = universe.world.add_component(
131 engine::ecs::component::TransformComponent::new().with_position(group_x, 0.0, 0.0),
132 );
133 let _ = universe.attach(bg_root, group_tx);
134
135 for cluster_i in 0..cluster_count {
136 let seed = (0xC10u32 ^ (group_i as u32).wrapping_mul(0x9E37_79B9))
137 ^ (cluster_i.wrapping_mul(7919));
138
139 let cx = (rand01(seed ^ 0xA341_316C) - 0.5) * 18.0;
140 let cy = (rand01(seed ^ 0xC801_3EA4) - 0.5) * 10.0 + 2.0;
141 let cz = -18.0 - rand01(seed ^ 0xB529_7A4D) * 26.0;
142
143 let center_tx = universe.world.add_component(
144 engine::ecs::component::TransformComponent::new().with_position(cx, cy, cz),
145 );
146 let _ = universe.attach(group_tx, center_tx);
147
148 let puffs = 18u32;
149 for puff_i in 0..puffs {
150 let puff_seed = seed ^ puff_i.wrapping_mul(1_103_515_245);
151
152 // Slightly ellipsoidal distribution.
153 let ox = (rand01(puff_seed ^ 0x68bc_21eb) - 0.5) * 7.0;
154 let oy = (rand01(puff_seed ^ 0x02e5_be93) - 0.5) * 3.0;
155 let oz = (rand01(puff_seed ^ 0xa1d3_4f2b) - 0.5) * 7.0;
156
157 let base = 0.7 + rand01(puff_seed ^ 0x9e37_79b9) * 2.6;
158 let sx = base * (0.7 + rand01(puff_seed ^ 0x243f_6a88) * 0.8);
159 let sy = base * (0.6 + rand01(puff_seed ^ 0x85a3_08d3) * 0.9);
160 let sz = base * (0.7 + rand01(puff_seed ^ 0x1319_8a2e) * 0.8);
161
162 let tx = universe.world.add_component(
163 engine::ecs::component::TransformComponent::new()
164 .with_position(ox, oy, oz)
165 .with_scale(sx, sy, sz),
166 );
167 let renderable =
168 universe
169 .world
170 .add_component(engine::ecs::component::RenderableComponent::new(
171 engine::graphics::primitives::Renderable::new(
172 cube_mesh,
173 engine::graphics::primitives::MaterialHandle::TOON_MESH,
174 ),
175 ));
176
177 // Slight blue-grey variation.
178 let t = rand01(puff_seed ^ 0x7f4a_7c15);
179 let r = 0.55 + 0.10 * t;
180 let g = 0.58 + 0.10 * t;
181 let b = 0.66 + 0.12 * t;
182 let color = universe
183 .world
184 .add_component(engine::ecs::component::ColorComponent::rgba(r, g, b, 1.0));
185
186 let _ = universe.attach(center_tx, tx);
187 let _ = universe.attach(tx, renderable);
188 let _ = universe.attach(renderable, color);
189 }
190 }
191 }
192
193 // Foreground reference cube (should never be occluded by background depth).
194 let fg_tx = universe.world.add_component(
195 engine::ecs::component::TransformComponent::new()
196 .with_position(0.0, -0.5, -4.0)
197 .with_scale(1.2, 0.8, 1.2),
198 );
199 let fg_renderable =
200 universe
201 .world
202 .add_component(engine::ecs::component::RenderableComponent::new(
203 engine::graphics::primitives::Renderable::new(
204 cube_mesh,
205 engine::graphics::primitives::MaterialHandle::TOON_MESH,
206 ),
207 ));
208 let fg_color = universe
209 .world
210 .add_component(engine::ecs::component::ColorComponent::rgba(
211 0.9, 0.2, 0.2, 1.0,
212 ));
213
214 universe.add(fg_tx);
215 let _ = universe.attach(fg_tx, fg_renderable);
216 let _ = universe.attach(fg_renderable, fg_color);
217
218 // Foreground opaque floor: 16x16 larger white cubes, 10 units below the reference cube.
219 let floor_root = universe.world.add_component(
220 engine::ecs::component::TransformComponent::new().with_position(0.0, -10.5, -10.0),
221 );
222 universe.add(floor_root);
223
224 let spacing = 10_f32;
225 let half = 5_f32;
226 for x in 0..16u32 {
227 for z in 0..64u32 {
228 let px = (x as f32 - half) * spacing;
229 let pz = (z as f32 * -1.0 + half) * spacing;
230 let tx = universe.world.add_component(
231 engine::ecs::component::TransformComponent::new()
232 .with_position(px, -5.0, pz)
233 .with_scale(5.0, 0.5, 5.0),
234 );
235 let renderable =
236 universe
237 .world
238 .add_component(engine::ecs::component::RenderableComponent::new(
239 engine::graphics::primitives::Renderable::new(
240 cube_mesh,
241 engine::graphics::primitives::MaterialHandle::TOON_MESH,
242 ),
243 ));
244 let color = universe
245 .world
246 .add_component(engine::ecs::component::ColorComponent::rgba(
247 1.0, 1.0, 1.0, 1.0,
248 ));
249
250 let _ = universe.attach(floor_root, tx);
251 let _ = universe.attach(tx, renderable);
252 let _ = universe.attach(renderable, color);
253 }
254 }
255
256 universe.systems.process_commands(
257 &mut universe.world,
258 &mut universe.visuals,
259 &mut universe.render_assets,
260 &mut universe.command_queue,
261 );
262
263 universe.enable_repl();
264 engine::Windowing::run_app(universe).expect("Windowing failed");
265}examples/animation-for-topology.rs (line 111)
80fn main() {
81 mittens_engine::example_support::ensure_model_assets();
82 utils::logger::init();
83
84 let world = engine::ecs::World::default();
85 let mut universe = engine::Universe::new(world);
86
87 // Minimal scene with a camera so the window opens.
88 let clear = universe
89 .world
90 .add_component(engine::ecs::component::BackgroundColorComponent::new());
91 let clear_c = universe
92 .world
93 .add_component(engine::ecs::component::ColorComponent::rgba(
94 0.06, 0.06, 0.07, 1.0,
95 ));
96 let _ = universe.world.add_child(clear, clear_c);
97 universe.add(clear);
98
99 // Input-driven camera rig.
100 let input = universe
101 .world
102 .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
103 let rig_transform = universe.world.add_component(
104 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 9.0),
105 );
106 let input_mode = universe.world.add_component(
107 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
108 );
109 let camera3d = universe.world.add_component(
110 engine::ecs::component::Camera3DComponent::new()
111 .with_far(250.0)
112 .with_fov(70.0),
113 );
114 let _ = universe.attach(input, input_mode);
115 let _ = universe.attach(input, rig_transform);
116 let _ = universe.attach(rig_transform, camera3d);
117
118 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
119 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
120 universe.add(input);
121
122 // Light so we can see non-emissive materials.
123 let light_tx = universe.world.add_component(
124 engine::ecs::component::TransformComponent::new().with_position(2.0, 3.5, 6.0),
125 );
126 let light = universe.world.add_component(
127 engine::ecs::component::PointLightComponent::new()
128 .with_distance(30.0)
129 .with_color(1.0, 1.0, 1.0),
130 );
131 let _ = universe.attach(light_tx, light);
132 universe.add(light_tx);
133
134 // ClockComponent drives the animation timeline in beats.
135 let clock = universe
136 .world
137 .add_component(engine::ecs::component::ClockComponent::new().with_bpm(140.0));
138 universe.add(clock);
139
140 // Root for all visualization objects.
141 let viz_root = universe.world.add_component(
142 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 0.0),
143 );
144 universe.add(viz_root);
145
146 let anchor_count = 16usize;
147 let cube_pool_size = 8usize;
148
149 // Three grids side-by-side.
150 let layout_a = GridLayout {
151 origin: (-6.0, 0.0, 0.0),
152 spacing: 1.1,
153 };
154 let layout_b = GridLayout {
155 origin: (0.0, 0.0, 0.0),
156 spacing: 1.1,
157 };
158 let layout_c = GridLayout {
159 origin: (6.0, 0.0, 0.0),
160 spacing: 1.1,
161 };
162
163 // --- Grid A: detach + re-attach (reparent) ---
164 let grid_a_root = universe
165 .world
166 .add_component(engine::ecs::component::TransformComponent::new());
167 let _ = universe.attach(viz_root, grid_a_root);
168
169 let mut anchors_a: Vec<engine::ecs::ComponentId> = Vec::with_capacity(anchor_count);
170 for i in 0..anchor_count {
171 let (x, y, z) = grid_anchor_local(layout_a, i);
172 let anchor = universe.world.add_component(
173 engine::ecs::component::TransformComponent::new().with_position(x, y, z),
174 );
175 let _ = universe.attach(grid_a_root, anchor);
176 anchors_a.push(anchor);
177 spawn_emissive_marker_cube(
178 &mut universe,
179 anchor,
180 (0.0, -0.35, 0.0),
181 0.06,
182 [0.18, 0.18, 0.22, 1.0],
183 );
184 }
185
186 let mut cubes_a: Vec<engine::ecs::ComponentId> = Vec::with_capacity(cube_pool_size);
187 for i in 0..cube_pool_size {
188 let t = (i as f32) / ((cube_pool_size - 1) as f32).max(1.0);
189 let rgba = [0.10, 0.40 + 0.50 * t, 0.90 - 0.70 * t, 1.0];
190 cubes_a.push(spawn_detached_cube_prefab(&mut universe, 0.22, rgba));
191 }
192
193 let anim_a = universe
194 .world
195 .add_component(engine::ecs::component::AnimationComponent::new());
196 for i in 0..anchor_count {
197 let cube = cubes_a[i % cube_pool_size];
198 let parent = anchors_a[i];
199
200 // Explicit detach+attach to test topology changes via animations.
201
202 // Put them on separate keyframes so ordering is time-deterministic.
203 let beat_detach = i as f64;
204 let beat_attach = i as f64 + 0.05;
205
206 let kf_detach = universe
207 .world
208 .add_component(engine::ecs::component::KeyframeComponent::new(beat_detach));
209 let _ = universe.attach(anim_a, kf_detach);
210 let detach_action =
211 universe
212 .world
213 .add_component(engine::ecs::component::ActionComponent::new(
214 engine::ecs::IntentValue::Detach {
215 component_ids: vec![cube],
216 },
217 ));
218 let _ = universe.attach(kf_detach, detach_action);
219
220 let kf_attach = universe
221 .world
222 .add_component(engine::ecs::component::KeyframeComponent::new(beat_attach));
223 let _ = universe.attach(anim_a, kf_attach);
224 let attach_action =
225 universe
226 .world
227 .add_component(engine::ecs::component::ActionComponent::new(
228 engine::ecs::IntentValue::Attach {
229 parents: vec![parent],
230 child: cube,
231 },
232 ));
233 let _ = universe.attach(kf_attach, attach_action);
234 }
235 universe.add(anim_a);
236
237 // --- Grid B: continuously spawn (attach_clone) + delete-behind (remove_child) ---
238 //
239 // Uses the new actions:
240 // - `Action::attach_clone(parent, prefab_root)`
241 // - `Action::remove_child(parent, index)`
242 //
243 // This avoids needing a pre-built pool of cube ComponentIds.
244 let grid_b_root = universe
245 .world
246 .add_component(engine::ecs::component::TransformComponent::new());
247 let _ = universe.attach(viz_root, grid_b_root);
248
249 let mut anchors_b: Vec<engine::ecs::ComponentId> = Vec::with_capacity(anchor_count);
250 for i in 0..anchor_count {
251 let (x, y, z) = grid_anchor_local(layout_b, i);
252 let anchor = universe.world.add_component(
253 engine::ecs::component::TransformComponent::new().with_position(x, y, z),
254 );
255 let _ = universe.attach(grid_b_root, anchor);
256 anchors_b.push(anchor);
257
258 // Marker is attached under the grid root (not under the anchor), so anchor child index 0
259 // remains reserved for the dynamic cube subtree.
260 spawn_emissive_marker_cube(
261 &mut universe,
262 grid_b_root,
263 (x, y - 0.35, z),
264 0.06,
265 [0.22, 0.18, 0.18, 1.0],
266 );
267 }
268
269 // Detached prefab that will be cloned on demand (reddish cube).
270 let prefab_b = spawn_detached_cube_prefab(&mut universe, 0.20, [0.90, 0.25, 0.15, 1.0]);
271
272 let steps_b = 32usize;
273 let window = 8usize;
274
275 let anim_b = universe
276 .world
277 .add_component(engine::ecs::component::AnimationComponent::new());
278
279 // Phase 1: each beat, spawn a cube under an anchor; delete-behind by removing child(0).
280 for i in 0..steps_b {
281 let parent = anchors_b[i % anchor_count];
282
283 let beat_attach = i as f64;
284 let beat_remove = i as f64 + 0.05;
285
286 let kf_attach = universe
287 .world
288 .add_component(engine::ecs::component::KeyframeComponent::new(beat_attach));
289 let _ = universe.attach(anim_b, kf_attach);
290
291 let attach_action =
292 universe
293 .world
294 .add_component(engine::ecs::component::ActionComponent::new(
295 engine::ecs::IntentValue::AttachClone {
296 parents: vec![parent],
297 prefab_root: prefab_b,
298 },
299 ));
300 let _ = universe.attach(kf_attach, attach_action);
301
302 if i >= window {
303 let remove_parent = anchors_b[(i - window) % anchor_count];
304 let kf_remove = universe
305 .world
306 .add_component(engine::ecs::component::KeyframeComponent::new(beat_remove));
307 let _ = universe.attach(anim_b, kf_remove);
308
309 let remove_action =
310 universe
311 .world
312 .add_component(engine::ecs::component::ActionComponent::new(
313 engine::ecs::IntentValue::RemoveChild {
314 parents: vec![remove_parent],
315 index: 0,
316 },
317 ));
318 let _ = universe.attach(kf_remove, remove_action);
319 }
320 }
321
322 // Phase 2: cleanup tail so the loop doesn't accumulate cubes.
323 for j in 0..window {
324 let remove_parent = anchors_b[(steps_b - window + j) % anchor_count];
325 let beat_remove = steps_b as f64 + j as f64 + 0.05;
326
327 let kf_remove = universe
328 .world
329 .add_component(engine::ecs::component::KeyframeComponent::new(beat_remove));
330 let _ = universe.attach(anim_b, kf_remove);
331
332 let remove_action =
333 universe
334 .world
335 .add_component(engine::ecs::component::ActionComponent::new(
336 engine::ecs::IntentValue::RemoveChild {
337 parents: vec![remove_parent],
338 index: 0,
339 },
340 ));
341 let _ = universe.attach(kf_remove, remove_action);
342 }
343
344 universe.add(anim_b);
345
346 // --- Grid C: move cubes via set_position (no topology changes) ---
347 let grid_c_root = universe
348 .world
349 .add_component(engine::ecs::component::TransformComponent::new());
350 let _ = universe.attach(viz_root, grid_c_root);
351
352 let mut anchors_c: Vec<(f32, f32, f32)> = Vec::with_capacity(anchor_count);
353 for i in 0..anchor_count {
354 let (x, y, z) = grid_anchor_local(layout_c, i);
355 anchors_c.push((x, y, z));
356
357 let anchor = universe.world.add_component(
358 engine::ecs::component::TransformComponent::new().with_position(x, y, z),
359 );
360 let _ = universe.attach(grid_c_root, anchor);
361 spawn_emissive_marker_cube(
362 &mut universe,
363 anchor,
364 (0.0, -0.35, 0.0),
365 0.06,
366 [0.18, 0.22, 0.18, 1.0],
367 );
368 }
369
370 let mut cubes_c: Vec<engine::ecs::ComponentId> = Vec::with_capacity(cube_pool_size);
371 for i in 0..cube_pool_size {
372 let t = (i as f32) / ((cube_pool_size - 1) as f32).max(1.0);
373 let rgba = [0.25, 0.85 - 0.55 * t, 0.25 + 0.55 * t, 1.0];
374 let cube_root = spawn_detached_cube_prefab(&mut universe, 0.22, rgba);
375 let _ = universe.attach(grid_c_root, cube_root);
376 cubes_c.push(cube_root);
377 }
378
379 let anim_c = universe
380 .world
381 .add_component(engine::ecs::component::AnimationComponent::new());
382 for i in 0..anchor_count {
383 let kf = universe
384 .world
385 .add_component(engine::ecs::component::KeyframeComponent::new(i as f64));
386 let _ = universe.attach(anim_c, kf);
387
388 let cube = cubes_c[i % cube_pool_size];
389 let (x, y, z) = anchors_c[i];
390 let setpos_action =
391 universe
392 .world
393 .add_component(engine::ecs::component::ActionComponent::new(
394 engine::ecs::IntentValue::SetPosition {
395 component_ids: vec![cube],
396 position: [x, y, z],
397 },
398 ));
399 let _ = universe.attach(kf, setpos_action);
400 }
401 universe.add(anim_c);
402
403 universe.systems.process_commands(
404 &mut universe.world,
405 &mut universe.visuals,
406 &mut universe.render_assets,
407 &mut universe.command_queue,
408 );
409
410 engine::Windowing::run_app(universe).expect("Windowing failed");
411}examples/animation-example.rs (line 37)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 let world = engine::ecs::World::default();
11 let mut universe = engine::Universe::new(world);
12
13 // Minimal scene with a camera so the window opens.
14 let clear = universe
15 .world
16 .add_component(engine::ecs::component::BackgroundColorComponent::new());
17 let clear_c = universe
18 .world
19 .add_component(engine::ecs::component::ColorComponent::rgba(
20 0.08, 0.08, 0.08, 1.0,
21 ));
22 let _ = universe.world.add_child(clear, clear_c);
23 universe.add(clear);
24
25 // Input-driven camera rig.
26 let input = universe
27 .world
28 .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
29 let rig_transform = universe.world.add_component(
30 engine::ecs::component::TransformComponent::new().with_position(2.0, 0.0, 7.0),
31 );
32 let input_mode = universe.world.add_component(
33 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
34 );
35 let camera3d = universe.world.add_component(
36 engine::ecs::component::Camera3DComponent::new()
37 .with_far(250.0)
38 .with_fov(70.0),
39 );
40 let _ = universe.attach(input, input_mode);
41 let _ = universe.attach(input, rig_transform);
42 let _ = universe.attach(rig_transform, camera3d);
43
44 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
45 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
46 universe.add(input);
47
48 // Light so we can see non-emissive materials (even though our cubes are emissive).
49 let light_tx = universe.world.add_component(
50 engine::ecs::component::TransformComponent::new().with_position(0.0, 2.5, 4.0),
51 );
52 let light = universe.world.add_component(
53 engine::ecs::component::PointLightComponent::new()
54 .with_distance(25.0)
55 .with_color(1.0, 1.0, 1.0),
56 );
57 let _ = universe.attach(light_tx, light);
58 universe.add(light_tx);
59
60 // ClockComponent sets global tempo.
61 let clock = universe
62 .world
63 .add_component(engine::ecs::component::ClockComponent::new().with_bpm(128.0));
64 universe.add(clock);
65
66 // Audio output + oscillators driven by scheduled actions.
67 let audio_out = universe
68 .world
69 .add_component(engine::ecs::component::AudioOutputComponent::new());
70 universe.add(audio_out);
71
72 // Noise oscillator: short white-noise hits.
73 let osc_noise = engine::ecs::component::AudioOscillator::noise()
74 .with_frequency(0.0)
75 .with_amplitude(0.06)
76 .with_enabled(false);
77 let osc_noise_comp =
78 universe
79 .world
80 .add_component(engine::ecs::component::AudioOscillatorComponent::single(
81 osc_noise,
82 ));
83 let _ = universe.attach(audio_out, osc_noise_comp);
84
85 // Drum oscillator: retriggers (phase + sweep) every enable.
86 let osc_drum = engine::ecs::component::AudioOscillator::drum()
87 // A low-ish pitch scale; actual pitch is set by scheduled notes.
88 .with_frequency(32.0)
89 .with_amplitude(0.40)
90 .with_enabled(false);
91 let osc_drum_comp =
92 universe
93 .world
94 .add_component(engine::ecs::component::AudioOscillatorComponent::single(
95 osc_drum,
96 ));
97
98 let _ = universe.attach(audio_out, osc_drum_comp);
99
100 // --- Visual layout helpers ---
101 fn spawn_text(universe: &mut engine::Universe, pos: (f32, f32, f32), scale: f32, text: &str) {
102 let tx = universe.world.add_component(
103 engine::ecs::component::TransformComponent::new()
104 .with_position(pos.0, pos.1, pos.2)
105 .with_scale(scale, scale, 1.0),
106 );
107 let t =
108 universe
109 .world
110 .add_component(engine::ecs::component::TextComponent::with_word_wrap(
111 text, 38,
112 ));
113 let _ = universe.attach(tx, t);
114 universe.add(tx);
115 }
116
117 fn spawn_emissive_cube(
118 universe: &mut engine::Universe,
119 parent: engine::ecs::ComponentId,
120 pos: (f32, f32, f32),
121 scale: f32,
122 rgba: [f32; 4],
123 ) {
124 let tx = universe.world.add_component(
125 engine::ecs::component::TransformComponent::new()
126 .with_position(pos.0, pos.1, pos.2)
127 .with_scale(scale, scale, scale),
128 );
129 let r = universe
130 .world
131 .add_component(engine::ecs::component::RenderableComponent::cube());
132 let c = universe
133 .world
134 .add_component(engine::ecs::component::ColorComponent::rgba(
135 rgba[0], rgba[1], rgba[2], rgba[3],
136 ));
137 let e = universe
138 .world
139 .add_component(engine::ecs::component::EmissiveComponent::on());
140 let _ = universe.attach(parent, tx);
141 let _ = universe.attach(tx, r);
142 let _ = universe.attach(r, c);
143 let _ = universe.attach(r, e);
144 }
145
146 fn spawn_op_cube(
147 universe: &mut engine::Universe,
148 parent: engine::ecs::ComponentId,
149 pos: (f32, f32, f32),
150 scale: f32,
151 base_rgba: [f32; 4],
152 ) -> engine::ecs::ComponentId {
153 let tx = universe.world.add_component(
154 engine::ecs::component::TransformComponent::new()
155 .with_position(pos.0, pos.1, pos.2)
156 .with_scale(scale, scale, scale),
157 );
158 let r = universe
159 .world
160 .add_component(engine::ecs::component::RenderableComponent::cube());
161 let c = universe
162 .world
163 .add_component(engine::ecs::component::ColorComponent::rgba(
164 base_rgba[0],
165 base_rgba[1],
166 base_rgba[2],
167 base_rgba[3],
168 ));
169 let e = universe
170 .world
171 .add_component(engine::ecs::component::EmissiveComponent::on());
172
173 let _ = universe.attach(parent, tx);
174 let _ = universe.attach(tx, r);
175 let _ = universe.attach(r, c);
176 let _ = universe.attach(r, e);
177
178 tx
179 }
180
181 // --- HUD / lanes ---
182 let lane_x = -2.6_f32;
183 let lane_title_z = -0.4_f32;
184 let lane_cfg_z = -0.4_f32;
185 let _lane_cube_z = -0.8_f32;
186 let drum_y = 0.9_f32;
187 let noise_y = -0.4_f32;
188
189 spawn_text(
190 &mut universe,
191 (lane_x, drum_y + 0.55, lane_title_z),
192 0.09,
193 "Audio Source A: Drum (kick)",
194 );
195 spawn_text(
196 &mut universe,
197 (lane_x, drum_y + 0.25, lane_cfg_z),
198 0.07,
199 "type=Drum\namp=0.40\nbase_freq=32Hz\nkick: C0 dur=0.12 vel=0.90\nlookahead=0.10s",
200 );
201
202 spawn_text(
203 &mut universe,
204 (lane_x, noise_y + 0.55, lane_title_z),
205 0.09,
206 "Audio Source B: Noise",
207 );
208 spawn_text(
209 &mut universe,
210 (lane_x, noise_y + 0.25, lane_cfg_z),
211 0.07,
212 "type=Noise\namp=0.06\nnoise: C9 dur=0.06 vel=0.25\noffset=+0.5 beats\nlookahead=0.10s",
213 );
214
215 // Representative cubes for the two audio sources.
216 let viz_root = universe.world.add_component(
217 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 0.0),
218 );
219 universe.add(viz_root);
220 spawn_emissive_cube(
221 &mut universe,
222 viz_root,
223 (lane_x - 0.28, drum_y + 0.55, lane_title_z),
224 0.16,
225 [1.00, 0.90, 0.10, 1.0],
226 );
227 spawn_emissive_cube(
228 &mut universe,
229 viz_root,
230 (lane_x - 0.28, noise_y + 0.55, lane_title_z),
231 0.16,
232 [1.00, 1.00, 1.00, 1.0],
233 );
234
235 // Timeline roots so we can "reset all" via a single set_color action.
236 let kick_timeline_root = universe.world.add_component(
237 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 0.0),
238 );
239 let noise_timeline_root = universe.world.add_component(
240 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 0.0),
241 );
242 let _ = universe.attach(viz_root, kick_timeline_root);
243 let _ = universe.attach(viz_root, noise_timeline_root);
244
245 // Animation with 16 keyframes: each keyframe schedules a kick at offset 0.0,
246 // and a noise hit at offset 0.5.
247 let anim = universe
248 .world
249 .add_component(engine::ecs::component::AnimationComponent::new());
250
251 let kick_dur = 0.12_f32;
252 let noise_dur = 0.06_f32;
253
254 // Requested palette:
255 // - kick: dark yellow -> bright yellow
256 // - noise: grey -> white
257 let kick_dark = [0.35, 0.28, 0.02, 1.0];
258 let kick_bright = [1.00, 0.90, 0.10, 1.0];
259 let noise_dark = [0.35, 0.35, 0.35, 1.0];
260 let noise_bright = [1.00, 1.00, 1.00, 1.0];
261
262 let beat_spacing = 0.38_f32;
263 for i in 0..16 {
264 let kf_beat = i as f64;
265 let kf = universe
266 .world
267 .add_component(engine::ecs::component::KeyframeComponent::new(kf_beat));
268
269 let kick = universe
270 .world
271 .add_component(engine::ecs::component::ActionComponent::new(
272 engine::ecs::IntentValue::AudioSchedulePlay {
273 component_ids: vec![osc_drum_comp],
274 beat_offset: 0.0,
275 beat_context: None,
276 note: Some(
277 engine::ecs::component::MusicNote::c(0, kick_dur).with_velocity(0.9),
278 ),
279 gain: None,
280 rate: None,
281 duration: None,
282 },
283 ));
284
285 let noise = universe
286 .world
287 .add_component(engine::ecs::component::ActionComponent::new(
288 engine::ecs::IntentValue::AudioSchedulePlay {
289 component_ids: vec![osc_noise_comp],
290 beat_offset: 0.5,
291 beat_context: None,
292 note: Some(
293 engine::ecs::component::MusicNote::c(9, noise_dur).with_velocity(0.25),
294 ),
295 gain: None,
296 rate: None,
297 duration: None,
298 },
299 ));
300
301 let _ = universe.attach(anim, kf);
302 let _ = universe.attach(kf, kick);
303 let _ = universe.attach(kf, noise);
304
305 // Each keyframe drives visualization purely via normal actions:
306 // 1) reset all timeline cubes to their base colors
307 // 2) brighten the current beat's cubes
308 let reset_kick_lane =
309 universe
310 .world
311 .add_component(engine::ecs::component::ActionComponent::new(
312 engine::ecs::IntentValue::SetColor {
313 component_ids: vec![kick_timeline_root],
314 rgba: kick_dark,
315 },
316 ));
317 let reset_noise_lane =
318 universe
319 .world
320 .add_component(engine::ecs::component::ActionComponent::new(
321 engine::ecs::IntentValue::SetColor {
322 component_ids: vec![noise_timeline_root],
323 rgba: noise_dark,
324 },
325 ));
326 let _ = universe.attach(kf, reset_kick_lane);
327 let _ = universe.attach(kf, reset_noise_lane);
328
329 // Timeline cubes: one cube per scheduled audio operation.
330 // Kick (offset 0.0) on the drum lane.
331 let kick_x = (kf_beat as f32) * beat_spacing;
332 let kick_cube = spawn_op_cube(
333 &mut universe,
334 kick_timeline_root,
335 (kick_x, drum_y, -1.3),
336 0.10,
337 kick_dark,
338 );
339
340 // Noise (offset 0.5) on the noise lane.
341 let noise_x = ((kf_beat + 0.5) as f32) * beat_spacing;
342 let noise_cube = spawn_op_cube(
343 &mut universe,
344 noise_timeline_root,
345 (noise_x, noise_y, -1.3),
346 0.10,
347 noise_dark,
348 );
349
350 let brighten_kick =
351 universe
352 .world
353 .add_component(engine::ecs::component::ActionComponent::new(
354 engine::ecs::IntentValue::SetColor {
355 component_ids: vec![kick_cube],
356 rgba: kick_bright,
357 },
358 ));
359 let brighten_noise =
360 universe
361 .world
362 .add_component(engine::ecs::component::ActionComponent::new(
363 engine::ecs::IntentValue::SetColor {
364 component_ids: vec![noise_cube],
365 rgba: noise_bright,
366 },
367 ));
368 let _ = universe.attach(kf, brighten_kick);
369 let _ = universe.attach(kf, brighten_noise);
370 }
371
372 universe.add(anim);
373
374 universe.systems.process_commands(
375 &mut universe.world,
376 &mut universe.visuals,
377 &mut universe.render_assets,
378 &mut universe.command_queue,
379 );
380
381 engine::Windowing::run_app(universe).expect("Windowing failed");
382}Additional examples can be found in:
pub fn with_enabled(self, enabled: bool) -> Self
Sourcepub fn make_active_camera(&mut self, emit: &mut dyn SignalEmitter)
pub fn make_active_camera(&mut self, emit: &mut dyn SignalEmitter)
Ask the CameraSystem to make this the active camera.
Trait Implementations§
Source§impl Clone for Camera3DComponent
impl Clone for Camera3DComponent
Source§fn clone(&self) -> Camera3DComponent
fn clone(&self) -> Camera3DComponent
Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Component for Camera3DComponent
impl Component for Camera3DComponent
Source§fn name(&self) -> &'static str
fn name(&self) -> &'static str
Short debug/type name for this component kind (e.g. “transform”, “camera”).
fn as_any(&self) -> &dyn Any
fn as_any_mut(&mut self) -> &mut dyn Any
Source§fn init(&mut self, emit: &mut dyn SignalEmitter, component: ComponentId)
fn init(&mut self, emit: &mut dyn SignalEmitter, component: ComponentId)
Called when component is added to the World
Source§fn to_mms_ast(&self, _world: &World) -> ComponentExpression
fn to_mms_ast(&self, _world: &World) -> ComponentExpression
Encode this component as an MMS Component Expression AST node. Read more
fn set_id(&mut self, _component: ComponentId)
Source§fn cleanup(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
fn cleanup(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
Called when component is removed from the World.
Source§impl Debug for Camera3DComponent
impl Debug for Camera3DComponent
Auto Trait Implementations§
impl Freeze for Camera3DComponent
impl RefUnwindSafe for Camera3DComponent
impl Send for Camera3DComponent
impl Sync for Camera3DComponent
impl Unpin for Camera3DComponent
impl UnsafeUnpin for Camera3DComponent
impl UnwindSafe for Camera3DComponent
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.