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: boolIf true, rotations are applied in world axes (FPS-style). If false, rotations are applied in local space (current behavior).
Implementations§
Source§impl InputTransformModeComponent
impl InputTransformModeComponent
pub fn forward_y() -> Self
Sourcepub fn forward_z() -> Self
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
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}Additional examples can be found in:
- examples/button-press.rs
- examples/vtuber-example.rs
- examples/openxr.rs
- examples/transparent-cutout-example.rs
- examples/text-example.rs
- examples/gestures-and-gizmos.rs
- examples/background-example.rs
- examples/mesh-factory-example.rs
- examples/simple-demo.rs
- examples/background-occlusion-example.rs
- examples/raycast-topology-animation.rs
- examples/animation-for-topology.rs
- examples/vr-input.rs
- examples/font-example.rs
- examples/vtuber-joints-example.rs
- examples/animation-example.rs
- examples/folder-text.rs
- examples/collision-perimeter.rs
- examples/audio-graph-example.rs
- examples/gravity-fields.rs
pub fn with_rotation_disabled(self) -> Self
pub fn with_translation_basis_source(self, source: ComponentRef) -> Self
Sourcepub fn with_fps_rotation(self) -> Self
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
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}Additional examples can be found in:
Sourcepub fn with_roll_axis_y(self) -> Self
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
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}Additional examples can be found in:
- examples/button-press.rs
- examples/vtuber-example.rs
- examples/openxr.rs
- examples/transparent-cutout-example.rs
- examples/text-example.rs
- examples/gestures-and-gizmos.rs
- examples/background-example.rs
- examples/mesh-factory-example.rs
- examples/simple-demo.rs
- examples/background-occlusion-example.rs
- examples/raycast-topology-animation.rs
- examples/animation-for-topology.rs
- examples/vr-input.rs
- examples/font-example.rs
- examples/vtuber-joints-example.rs
- examples/animation-example.rs
- examples/folder-text.rs
- examples/collision-perimeter.rs
- examples/audio-graph-example.rs
- examples/gravity-fields.rs
pub fn with_roll_axis_z(self) -> Self
Trait Implementations§
Source§impl Clone for InputTransformModeComponent
impl Clone for InputTransformModeComponent
Source§fn clone(&self) -> InputTransformModeComponent
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)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Component for InputTransformModeComponent
impl Component for InputTransformModeComponent
Source§fn name(&self) -> &'static str
fn name(&self) -> &'static str
Short debug/type name for this component kind (e.g. “transform”, “camera”).
fn as_any(&self) -> &dyn Any
fn as_any_mut(&mut self) -> &mut dyn Any
Source§fn to_mms_ast(&self, _world: &World) -> ComponentExpression
fn to_mms_ast(&self, _world: &World) -> ComponentExpression
Encode this component as an MMS Component Expression AST node. Read more
fn set_id(&mut self, _component: ComponentId)
Source§fn init(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
fn init(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
Called when component is added to the World
Source§fn cleanup(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
fn cleanup(&mut self, _emit: &mut dyn SignalEmitter, _component: ComponentId)
Called when component is removed from the World.
Source§impl Debug for InputTransformModeComponent
impl Debug for InputTransformModeComponent
Auto Trait Implementations§
impl Freeze for InputTransformModeComponent
impl RefUnwindSafe for InputTransformModeComponent
impl Send for InputTransformModeComponent
impl Sync for InputTransformModeComponent
impl Unpin for InputTransformModeComponent
impl UnsafeUnpin for InputTransformModeComponent
impl UnwindSafe for InputTransformModeComponent
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.