Skip to main content

InputTransformModeComponent

Struct InputTransformModeComponent 

Source
pub struct InputTransformModeComponent {
    pub forward_axis: ForwardAxis,
    pub roll_axis: RollAxis,
    pub rotation_enabled: bool,
    pub translation_basis_source: Option<ComponentRef>,
    pub fps_rotation: bool,
}
Expand description

Input mode component that controls which axis is treated as “forward” for WASD.

Intended topology: InputComponent -> (InputTransformModeComponent?) InputComponent -> TransformComponent

Fields§

§forward_axis: ForwardAxis§roll_axis: RollAxis§rotation_enabled: bool§translation_basis_source: Option<ComponentRef>§fps_rotation: bool

If true, rotations are applied in world axes (FPS-style). If false, rotations are applied in local space (current behavior).

Implementations§

Source§

impl InputTransformModeComponent

Source

pub fn forward_y() -> Self

Source

pub fn forward_z() -> Self

Examples found in repository?
examples/example_util/mod.rs (line 40)
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 30)
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 28)
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 403)
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 392)
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 25)
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_rotation_disabled(self) -> Self

Source

pub fn with_translation_basis_source(self, source: ComponentRef) -> Self

Source

pub fn with_fps_rotation(self) -> Self

Examples found in repository?
examples/opacity-example.rs (line 405)
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}
More examples
Hide additional examples
examples/pointer-events.rs (line 393)
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 483)
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/vtuber-example.rs (line 41)
11fn main() {
12    mittens_engine::example_support::ensure_model_assets();
13    utils::logger::init();
14
15    let world = engine::ecs::World::default();
16    let mut universe = engine::Universe::new(world);
17
18    // Light pink background.
19    let background = universe
20        .world
21        .add_component(BackgroundColorComponent::new());
22    let background_c = universe
23        .world
24        .add_component(ColorComponent::rgba(1.0, 0.82, 0.90, 1.0));
25    let _ = universe.world.add_child(background, background_c);
26    universe.add(background);
27
28    // Small ambient so shadowed areas aren't pure black.
29    let ambient = universe
30        .world
31        .add_component(AmbientLightComponent::rgb(0.10, 0.10, 0.12));
32    universe.add(ambient);
33
34    // --- Camera rig (WASD + mouse) ---
35    // InputComponent is the root, and it owns a Transform (the camera rig).
36    let input = universe
37        .world
38        .add_component(InputComponent::new().with_speed(1.5));
39    let input_mode = universe.world.add_component(
40        InputTransformModeComponent::forward_z()
41            .with_fps_rotation()
42            .with_roll_axis_y(),
43    );
44    let _ = universe.attach(input, input_mode);
45
46    // Start slightly pulled back looking towards the origin.
47    let rig_transform = universe
48        .world
49        .add_component(TransformComponent::new().with_position(0.0, 0.0, 6.0));
50    let _ = universe.attach(input, rig_transform);
51
52    let camera3d = universe.world.add_component(Camera3DComponent::new());
53    let _ = universe.attach(rig_transform, camera3d);
54
55    // Topology: I { T { C3D } } — add a small camera-attached controls hint.
56    example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
57
58    universe.add(input);
59
60    // --- lighting ---
61    // Directional key light (slightly down + forward Z).
62    let sun = universe.world.add_component(
63        DirectionalLightComponent::new()
64            .with_intensity(1.2)
65            .with_color(1.0, 0.98, 0.94),
66    );
67    let sun_dir = universe
68        .world
69        .add_component(TransformComponent::new().with_position(0.0, -0.35, 1.0));
70    let _ = universe.attach(sun_dir, sun);
71    universe.add(sun_dir);
72
73    let light_transform = universe.world.add_component(
74        TransformComponent::new()
75            .with_position(1.0, 6.0, 3.0)
76            .with_scale(0.1, 0.1, 0.1),
77    );
78    let light = universe.world.add_component(
79        engine::ecs::component::PointLightComponent::new()
80            .with_distance(120.0)
81            .with_color(1.0, 1.0, 1.0),
82    );
83    let _ = universe.attach(light_transform, light);
84    universe.add(light_transform);
85
86    // --- VTuber model ---
87    let model_root = universe.world.add_component(TransformComponent::new());
88    let model = universe
89        .world
90        .add_component(GLTFComponent::new("assets/models/pc-rei.hoodie.glb"));
91    // emissive for pc-rei
92    let emissive = universe
93        .world
94        .add_component(engine::ecs::component::EmissiveComponent::on());
95
96    let _ = universe.attach(model, emissive);
97
98    let xr_input = universe.world.add_component(InputXRComponent::on());
99    let xr_gamepad = universe
100        .world
101        .add_component(engine::ecs::component::InputXRGamepadComponent::new().speed(1.5));
102    let xr_head = universe.world.add_component(TransformComponent::new());
103    let xr_camera = universe.world.add_component(CameraXRComponent::on());
104    let _ = universe.attach(xr_input, xr_head);
105    let _ = universe.attach(xr_input, xr_gamepad);
106    let _ = universe.attach(xr_head, xr_camera);
107    let _ = universe.attach(xr_head, model_root);
108
109    let _ = universe.attach(model_root, model);
110    universe.add(xr_input);
111    universe.add(model_root);
112
113    // --- Background clouds (occluded + lit) ---
114    let bg_root = universe.world.add_component(
115        engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
116    );
117    universe.add(bg_root);
118    let mut cloud_params = example_util::CloudRingParams::default();
119    cloud_params.cloud_count = 8; // +3 clusters
120    cloud_params.angle_jitter = 0.35;
121    cloud_params.high_y_probability = 0.5;
122    cloud_params.high_y_multiplier = 1.5;
123    cloud_params.seed = 0x57_55_B0_01u32;
124    example_util::spawn_cloud_ring(&mut universe, bg_root, cloud_params);
125
126    // --- Simple environment ---
127    let spawn_cube = |universe: &mut engine::Universe,
128                      position: (f32, f32, f32),
129                      scale: (f32, f32, f32),
130                      color: (f32, f32, f32, f32)| {
131        let transform = universe.world.add_component(
132            TransformComponent::new()
133                .with_position(position.0, position.1, position.2)
134                .with_scale(scale.0, scale.1, scale.2),
135        );
136        let renderable = universe.world.add_component(RenderableComponent::cube());
137        let color = universe
138            .world
139            .add_component(ColorComponent::rgba(color.0, color.1, color.2, color.3));
140
141        let _ = universe.attach(transform, renderable);
142        let _ = universe.attach(renderable, color);
143
144        universe.add(transform);
145    };
146
147    // floor
148    spawn_cube(
149        &mut universe,
150        (0.0, -0.05, 0.0),
151        (10.0, 0.1, 10.0),
152        (0.92, 0.92, 0.92, 1.0),
153    );
154
155    // back wall
156    spawn_cube(
157        &mut universe,
158        (0.0, 1.5, -5.0),
159        (10.0, 3.0, 1.0),
160        (0.95, 0.94, 0.96, 1.0),
161    );
162
163    // desk
164    spawn_cube(
165        &mut universe,
166        (0.0, 0.35, 1.0),
167        (1.0, 0.75, 0.5),
168        (0.75, 0.70, 0.65, 1.0),
169    );
170
171    let xr_root = universe
172        .world
173        .add_component(engine::ecs::component::XrComponent::on());
174    universe.add(xr_root);
175
176    universe.systems.process_commands(
177        &mut universe.world,
178        &mut universe.visuals,
179        &mut universe.render_assets,
180        &mut universe.command_queue,
181    );
182
183    universe.enable_repl();
184    engine::Windowing::run_app(universe).expect("Windowing failed");
185}
examples/gestures-and-gizmos.rs (line 99)
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/raycast-topology-animation.rs (line 148)
111fn main() {
112    mittens_engine::example_support::ensure_model_assets();
113    utils::logger::init();
114
115    let world = engine::ecs::World::default();
116    let mut universe = engine::Universe::new(world);
117
118    // Background.
119    let bg_color = universe
120        .world
121        .add_component(engine::ecs::component::BackgroundColorComponent::new());
122    let bg_color_c = universe
123        .world
124        .add_component(engine::ecs::component::ColorComponent::rgba(
125            0.1, 0.1, 0.1, 1.0,
126        ));
127    let _ = universe.world.add_child(bg_color, bg_color_c);
128    universe.add(bg_color);
129
130    // Camera rig so we can see the scene.
131    let input = universe
132        .world
133        .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
134
135    let rig_transform = universe.world.add_component(
136        engine::ecs::component::TransformComponent::new()
137            .with_position(0.0, 2.0, 8.0)
138            .with_rotation_euler(-0.25, 0.0, 0.0),
139    );
140
141    let camera3d = universe
142        .world
143        .add_component(engine::ecs::component::Camera3DComponent::new());
144
145    let input_mode = universe.world.add_component(
146        engine::ecs::component::InputTransformModeComponent::forward_z()
147            .with_roll_axis_y()
148            .with_fps_rotation(),
149    );
150
151    let _ = universe.attach(input, input_mode);
152    let _ = universe.attach(input, rig_transform);
153    let _ = universe.attach(rig_transform, camera3d);
154
155    // Topology: I { T { C3D } } — add a small camera-attached controls hint.
156    example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
157    universe.add(input);
158
159    // Two rotating anchor transforms that the raycaster will be reparented under.
160    // Each ring has its own anchor at a different height.
161    let anchor_a = universe.world.add_component(
162        engine::ecs::component::TransformComponent::new().with_position(0.0, 1.0, 0.0),
163    );
164    let anchor_b = universe.world.add_component(
165        engine::ecs::component::TransformComponent::new().with_position(0.0, 2.2, 0.0),
166    );
167
168    // Visual markers for A/B.
169    fn marker(universe: &mut engine::Universe, parent: engine::ecs::ComponentId, rgba: [f32; 4]) {
170        let r = universe
171            .world
172            .add_component(engine::ecs::component::RenderableComponent::cube());
173        let rc = universe
174            .world
175            .add_component(engine::ecs::component::RaycastableComponent::disabled());
176        let c = universe
177            .world
178            .add_component(engine::ecs::component::ColorComponent::rgba(
179                rgba[0], rgba[1], rgba[2], rgba[3],
180            ));
181        let e = universe
182            .world
183            .add_component(engine::ecs::component::EmissiveComponent::on());
184        let t = universe.world.add_component(
185            engine::ecs::component::TransformComponent::new().with_scale(0.15, 0.15, 0.15),
186        );
187        let _ = universe.attach(parent, t);
188        let _ = universe.attach(t, r);
189        let _ = universe.attach(r, rc);
190        let _ = universe.attach(r, c);
191        let _ = universe.attach(r, e);
192    }
193
194    marker(&mut universe, anchor_a, [0.9, 0.2, 0.2, 1.0]);
195    marker(&mut universe, anchor_b, [0.2, 0.2, 0.9, 1.0]);
196
197    universe.add(anchor_a);
198    universe.add(anchor_b);
199
200    // A ring of cubes around the origin to see which one gets hit.
201    fn ring_cube(
202        universe: &mut engine::Universe,
203        ring_root: engine::ecs::ComponentId,
204        x: f32,
205        y: f32,
206        z: f32,
207        rgba: [f32; 4],
208    ) {
209        let t = universe.world.add_component(
210            engine::ecs::component::TransformComponent::new()
211                .with_position(x, y, z)
212                .with_scale(0.35, 0.35, 0.35),
213        );
214        let r = universe
215            .world
216            .add_component(engine::ecs::component::RenderableComponent::cube());
217        let rc = universe
218            .world
219            .add_component(engine::ecs::component::RaycastableComponent::enabled());
220        let c = universe
221            .world
222            .add_component(engine::ecs::component::ColorComponent::rgba(
223                rgba[0], rgba[1], rgba[2], rgba[3],
224            ));
225        let _ = universe.attach(ring_root, t);
226        let _ = universe.attach(t, r);
227        let _ = universe.attach(r, rc);
228        let _ = universe.attach(r, c);
229    }
230
231    // Two rings: one per anchor height.
232    let n = 28;
233    let (radius_a, y_a) = (4.0, 1.0);
234    let (radius_b, y_b) = (2.6, 2.2);
235
236    let ring_a_root = universe
237        .world
238        .add_component(engine::ecs::component::TransformComponent::new());
239    let ring_b_root = universe
240        .world
241        .add_component(engine::ecs::component::TransformComponent::new());
242
243    for i in 0..n {
244        let a = (i as f32) * (std::f32::consts::TAU / (n as f32));
245        let (x, z) = (radius_a * a.cos(), radius_a * a.sin());
246        let color = if i % 2 == 0 {
247            [0.55, 0.20, 0.20, 1.0]
248        } else {
249            [0.20, 0.55, 0.20, 1.0]
250        };
251        ring_cube(&mut universe, ring_a_root, x, y_a, z, color);
252    }
253
254    for i in 0..n {
255        let a = (i as f32) * (std::f32::consts::TAU / (n as f32));
256        let (x, z) = (radius_b * a.cos(), radius_b * a.sin());
257        let color = if i % 2 == 0 {
258            [0.20, 0.25, 0.60, 1.0]
259        } else {
260            [0.20, 0.55, 0.55, 1.0]
261        };
262        ring_cube(&mut universe, ring_b_root, x, y_b, z, color);
263    }
264
265    // Init rings and attach scoped interaction handlers.
266    universe.add(ring_a_root);
267    universe.add(ring_b_root);
268    universe.add_signal_handler(
269        engine::ecs::SignalKind::RayIntersected,
270        ring_a_root,
271        ring_a_handler,
272    );
273    universe.add_signal_handler(
274        engine::ecs::SignalKind::RayIntersected,
275        ring_b_root,
276        ring_b_handler,
277    );
278
279    // The raycaster component we will move between parents.
280    // Source is inferred from topology:
281    // - Under transforms A/B (no camera child) => parent-forward (-Z)
282    // - Under camera rig transform (has camera child) => cursor-through-camera
283    let raycaster = universe.world.add_component(
284        engine::ecs::component::RayCastComponent::event_driven().with_max_distance(25.0),
285    );
286
287    // Global animation: move the raycaster between anchors.
288    // Loop length is 8 beats (we include a noop keyframe at beat 7.0 to force loop_len=8).
289    let anim_global = universe
290        .world
291        .add_component(engine::ecs::component::AnimationComponent::new());
292    {
293        // beat 0: attach to A.
294        let kf0 = universe
295            .world
296            .add_component(engine::ecs::component::KeyframeComponent::new(0.0));
297        let act0 = universe
298            .world
299            .add_component(engine::ecs::component::ActionComponent::new(
300                engine::ecs::IntentValue::Attach {
301                    parents: vec![anchor_a],
302                    child: raycaster,
303                },
304            ));
305        let _ = universe.attach(kf0, act0);
306        let _ = universe.attach(anim_global, kf0);
307
308        // beat 4: attach to B.
309        let kf4 = universe
310            .world
311            .add_component(engine::ecs::component::KeyframeComponent::new(4.0));
312        let act4 = universe
313            .world
314            .add_component(engine::ecs::component::ActionComponent::new(
315                engine::ecs::IntentValue::Attach {
316                    parents: vec![anchor_b],
317                    child: raycaster,
318                },
319            ));
320        let _ = universe.attach(kf4, act4);
321        let _ = universe.attach(anim_global, kf4);
322
323        // beat 7: noop to make loop_len=8.
324        let kf7 = universe
325            .world
326            .add_component(engine::ecs::component::KeyframeComponent::new(7.0));
327        let noop = universe
328            .world
329            .add_component(engine::ecs::component::ActionComponent::new(
330                engine::ecs::IntentValue::Noop,
331            ));
332        let _ = universe.attach(kf7, noop);
333        let _ = universe.attach(anim_global, kf7);
334    }
335    universe.add(anim_global);
336
337    // Ring A animation: rotate anchor A around Y (1 rev / 8 beats).
338    // Also triggers Action::raycast(raycaster) on downbeats in the first half: beats 0,1,2,3.
339    let anim_a = universe
340        .world
341        .add_component(engine::ecs::component::AnimationComponent::new());
342
343    // Ring B animation: rotate anchor B differently (yaw + pitch).
344    // Also triggers Action::raycast(raycaster) on offbeats in the second half: beats 4.5,5.5,6.5,7.5.
345    let anim_b = universe
346        .world
347        .add_component(engine::ecs::component::AnimationComponent::new());
348
349    let loop_beats = 8.0;
350    let steps = 64;
351
352    for step in 0..=steps {
353        let t = (step as f32) / (steps as f32);
354        let beat = (t as f64) * (loop_beats as f64);
355
356        // Keyframe beats are per-animation local beats.
357        let kf_a = universe
358            .world
359            .add_component(engine::ecs::component::KeyframeComponent::new(beat));
360        let kf_b = universe
361            .world
362            .add_component(engine::ecs::component::KeyframeComponent::new(beat));
363
364        // Anchor A rotation: smooth yaw.
365        let yaw_a = t * std::f32::consts::TAU;
366        let a_set = engine::ecs::component::ActionComponent::new(
367            engine::ecs::IntentValue::UpdateTransform {
368                component_ids: vec![anchor_a],
369                translation: [0.0, 1.0, 0.0],
370                rotation_quat_xyzw: quat_from_yaw(yaw_a),
371                scale: [1.0, 1.0, 1.0],
372            },
373        );
374        let a_set_id = universe.world.add_component(a_set);
375        let _ = universe.attach(kf_a, a_set_id);
376
377        // Anchor B rotation: yaw + pitch (different pattern).
378        let yaw_b = -t * std::f32::consts::TAU * 1.5;
379        let pitch_b = (t * std::f32::consts::TAU).sin() * 0.35;
380        let rot_b = quat_mul(quat_from_yaw(yaw_b), quat_from_pitch(pitch_b));
381        let b_set = engine::ecs::component::ActionComponent::new(
382            engine::ecs::IntentValue::UpdateTransform {
383                component_ids: vec![anchor_b],
384                translation: [0.0, 2.2, 0.0],
385                rotation_quat_xyzw: rot_b,
386                scale: [1.0, 1.0, 1.0],
387            },
388        );
389        let b_set_id = universe.world.add_component(b_set);
390        let _ = universe.attach(kf_b, b_set_id);
391
392        // Per-ring raycast patterns.
393        // A: downbeats in first half (0..4): step % 8 == 0 -> beats 0,1,2,3,4.
394        if beat < 4.0 && step % 8 == 0 {
395            let act = universe
396                .world
397                .add_component(engine::ecs::component::ActionComponent::new(
398                    engine::ecs::IntentValue::RequestRaycast {
399                        component_ids: vec![raycaster],
400                    },
401                ));
402            let _ = universe.attach(kf_a, act);
403        }
404
405        // B: offbeats in second half (4..8): step % 8 == 4 -> beats 0.5,1.5,...,7.5.
406        if beat >= 4.0 && step % 8 == 4 {
407            let act = universe
408                .world
409                .add_component(engine::ecs::component::ActionComponent::new(
410                    engine::ecs::IntentValue::RequestRaycast {
411                        component_ids: vec![raycaster],
412                    },
413                ));
414            let _ = universe.attach(kf_b, act);
415        }
416
417        let _ = universe.attach(anim_a, kf_a);
418        let _ = universe.attach(anim_b, kf_b);
419    }
420
421    universe.add(anim_a);
422    universe.add(anim_b);
423
424    // Process init-time registrations.
425    universe.systems.process_commands(
426        &mut universe.world,
427        &mut universe.visuals,
428        &mut universe.render_assets,
429        &mut universe.command_queue,
430    );
431
432    engine::Windowing::run_app(universe).expect("Windowing failed");
433}
Source

pub fn with_roll_axis_y(self) -> Self

Examples found in repository?
examples/example_util/mod.rs (line 40)
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 30)
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 28)
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 404)
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 394)
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 25)
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_roll_axis_z(self) -> Self

Trait Implementations§

Source§

impl Clone for InputTransformModeComponent

Source§

fn clone(&self) -> InputTransformModeComponent

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 InputTransformModeComponent

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 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 init(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)

Called when component is added to the World
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 InputTransformModeComponent

Source§

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

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

impl Default for InputTransformModeComponent

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