Skip to main content

Camera3DComponent

Struct Camera3DComponent 

Source
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: CameraTarget

Which 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: f32

Vertical field of view (degrees).

§z_near: f32§z_far: f32

Implementations§

Source§

impl Camera3DComponent

Source

pub const DEFAULT_FOV_Y_DEGREES: f32 = 60.0

Source

pub const DEFAULT_Z_NEAR: f32 = 0.1

Source

pub const DEFAULT_Z_FAR: f32 = 150.0

Source

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
Hide additional 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}
Source

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
Hide additional 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}
Source

pub fn with_near(self, z_near: f32) -> Self

Source

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
Hide additional 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}
Source

pub fn with_enabled(self, enabled: bool) -> Self

Source

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

Source§

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)

Performs copy-assignment from source. Read more
Source§

impl Component for Camera3DComponent

Source§

fn name(&self) -> &'static str

Short debug/type name for this component kind (e.g. “transform”, “camera”).
Source§

fn as_any(&self) -> &dyn Any

Source§

fn as_any_mut(&mut self) -> &mut dyn Any

Source§

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

Encode this component as an MMS Component Expression AST node. Read more
Source§

fn set_id(&mut self, _component: ComponentId)

Source§

fn cleanup(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)

Called when component is removed from the World.
Source§

fn encode(&self) -> HashMap<String, Value>

Encode component data to a HashMap for serialization. Read more
Source§

fn decode(&mut self, _data: &HashMap<String, Value>) -> Result<(), String>

Decode component data from a HashMap after deserialization. Read more
Source§

impl Debug for Camera3DComponent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Camera3DComponent

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

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>

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)

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)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more