pub struct InputXRComponent {
pub enabled: bool,
pub pose_valid: bool,
pub component_id: Option<ComponentId>,
}Expand description
Marker/config for an XR headset pose driver.
Semantics:
- Attach a
TransformComponentas a child of this component. - the active XR runtime will drive that transform child from the headset/root pose.
Fields§
§enabled: bool§pose_valid: boolRuntime-only: true after a valid headset pose was applied this frame.
component_id: Option<ComponentId>Implementations§
Source§impl InputXRComponent
impl InputXRComponent
pub fn new(enabled: bool) -> Self
Sourcepub fn on() -> Self
pub fn on() -> Self
Examples found in repository?
examples/vtuber-example.rs (line 98)
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}More examples
examples/openxr.rs (line 139)
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 let background = universe
14 .world
15 .add_component(engine::ecs::component::BackgroundColorComponent::new());
16 let background_c = universe
17 .world
18 .add_component(engine::ecs::component::ColorComponent::rgba(
19 0.3, 0.1, 1.0, 1.0,
20 ));
21 let _ = universe.world.add_child(background, background_c);
22 universe.add(background);
23
24 // --- Camera rig (WASD/QE) ---
25 // Keep this similar to the main demo so we can fly around the cube field.
26 let input = universe
27 .world
28 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
29 let input_mode = universe.world.add_component(
30 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
31 );
32 let _ = universe.attach(input, input_mode);
33
34 // Start pulled back so the grid is in view.
35 let rig_transform = universe.world.add_component(
36 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 4.0),
37 );
38 let _ = universe.attach(input, rig_transform);
39
40 let camera3d = universe
41 .world
42 .add_component(engine::ecs::component::Camera3DComponent::new());
43 let _ = universe.attach(rig_transform, camera3d);
44
45 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
46 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
47
48 // Simple point light so the toon shader reads well.
49 let light = universe.world.add_component(
50 engine::ecs::component::PointLightComponent::new()
51 .with_distance(50.0)
52 .with_color(1.0, 1.0, 1.0),
53 );
54 let light_transform = universe.world.add_component(
55 engine::ecs::component::TransformComponent::new().with_position(0.0, 5.0, 2.0),
56 );
57 let _ = universe.attach(light_transform, light);
58
59 universe.add(input);
60 universe.add(light_transform);
61
62 // --- 16x16x16 cube grid ---
63 let cube_mesh = universe
64 .render_assets
65 .get_mesh(engine::graphics::BuiltinMeshType::Cube);
66
67 let n: usize = 32;
68 let cube_scale: f32 = 0.10;
69 let gap: f32 = 0.50;
70 let step: f32 = cube_scale + gap;
71
72 // Center positions around 0 by subtracting half the extent (in steps).
73 let half_extent_x = (n as f32 - 1.0) * step * 0.5;
74 let half_extent_y = (n as f32 - 1.0) * step * 0.5;
75 let half_extent_z = (n as f32 - 1.0) * step * 0.5;
76
77 // Move the whole container up/back based on its content size, plus the requested offsets.
78 // - up by +0.5 and by half the content height
79 // - back by -(0.5 + 1.0) and by half the content depth
80 let container_offset_y = half_extent_y + 0.5;
81 let container_offset_z = -(half_extent_z + 1.0 + 0.5);
82
83 let container = universe.world.add_component(
84 engine::ecs::component::TransformComponent::new().with_position(
85 0.0,
86 container_offset_y,
87 container_offset_z,
88 ),
89 );
90
91 for z in 0..n {
92 for y in 0..n {
93 for x in 0..n {
94 let px = x as f32 * step - half_extent_x;
95 let py = y as f32 * step - half_extent_y;
96 let pz = z as f32 * step - half_extent_z;
97
98 let tx = universe.world.add_component(
99 engine::ecs::component::TransformComponent::new()
100 .with_position(px, py, pz)
101 .with_scale(cube_scale, cube_scale, cube_scale),
102 );
103 let renderable =
104 universe
105 .world
106 .add_component(engine::ecs::component::RenderableComponent::new(
107 engine::graphics::primitives::Renderable::new(
108 cube_mesh,
109 engine::graphics::primitives::MaterialHandle::TOON_MESH,
110 ),
111 ));
112
113 let denom = (n - 1) as f32;
114 let color = engine::ecs::component::ColorComponent::rgba(
115 f32::sin(x as f32 / 10.0),
116 y as f32 / denom,
117 z as f32 / denom,
118 1.0,
119 );
120 let color_c = universe.world.add_component(color);
121
122 let _ = universe.attach(container, tx);
123 let _ = universe.attach(tx, renderable);
124 let _ = universe.attach(renderable, color_c);
125 }
126 }
127 }
128
129 universe.add(container);
130 universe.systems.process_commands(
131 &mut universe.world,
132 &mut universe.visuals,
133 &mut universe.render_assets,
134 &mut universe.command_queue,
135 );
136
137 let xr_input = universe
138 .world
139 .add_component(engine::ecs::component::InputXRComponent::on());
140 let xr_gamepad = universe
141 .world
142 .add_component(engine::ecs::component::InputXRGamepadComponent::new().speed(1.5));
143 let xr_head = universe
144 .world
145 .add_component(engine::ecs::component::TransformComponent::new());
146 let camera_xr = universe
147 .world
148 .add_component(engine::ecs::component::CameraXRComponent::on());
149 let _ = universe.attach(xr_input, xr_head);
150 let _ = universe.attach(xr_input, xr_gamepad);
151 let _ = universe.attach(xr_head, camera_xr);
152 universe.add(xr_input);
153
154 // Add an OpenXR component so OpenXRSystem initializes and starts polling events.
155 let xr_root = universe
156 .world
157 .add_component(engine::ecs::component::XrComponent::on());
158 universe.add(xr_root);
159 universe.systems.process_commands(
160 &mut universe.world,
161 &mut universe.visuals,
162 &mut universe.render_assets,
163 &mut universe.command_queue,
164 );
165
166 universe.enable_repl();
167 engine::Windowing::run_app(universe).expect("Windowing failed");
168}examples/vr-input.rs (line 285)
186fn main() {
187 mittens_engine::example_support::ensure_model_assets();
188 utils::logger::init();
189
190 let options = match parse_options() {
191 Ok(options) => options,
192 Err(message) => {
193 eprintln!("{message}");
194 std::process::exit(2);
195 }
196 };
197
198 println!(
199 "[vr-input] xr controller rotation filter pipeline: {}",
200 if options.xr_controller_rotation_filter {
201 "enabled"
202 } else {
203 "disabled"
204 }
205 );
206
207 let world = engine::ecs::World::default();
208 let mut universe = engine::Universe::new(world);
209
210 let renderer_settings = universe
211 .world
212 .add_component(RendererSettingsComponent::msaa_off().with_window_size(320, 240));
213 universe.add(renderer_settings);
214
215 let render_graph = universe.world.add_component(RenderGraphComponent::new());
216 let emissive_pass = universe.world.add_component(EmissivePassComponent::new());
217 let blur_pass = universe.world.add_component(
218 BlurPassComponent::new()
219 .with_radius_ndc(0.06)
220 .with_half_res(true),
221 );
222 let bloom = universe.world.add_component(
223 BloomComponent::new()
224 .with_intensity(0.95)
225 .with_emissive_scale(1.2),
226 );
227 let _ = universe.attach(emissive_pass, blur_pass);
228 let _ = universe.attach(render_graph, emissive_pass);
229 let _ = universe.attach(render_graph, bloom);
230 universe.add(render_graph);
231
232 // Sky base.
233 let background = universe
234 .world
235 .add_component(BackgroundColorComponent::new());
236 let background_c = universe
237 .world
238 .add_component(ColorComponent::rgba(0.62, 0.80, 1.00, 1.0));
239 let _ = universe.world.add_child(background, background_c);
240 universe.add(background);
241
242 // Lighting for the model.
243 let ambient = universe
244 .world
245 .add_component(AmbientLightComponent::rgb(0.18, 0.18, 0.22));
246 universe.add(ambient);
247
248 let sun = universe.world.add_component(
249 DirectionalLightComponent::new()
250 .with_intensity(1.1)
251 .with_color(1.0, 0.98, 0.95),
252 );
253 let sun_dir = universe
254 .world
255 .add_component(TransformComponent::new().with_position(0.15, -0.45, 1.0));
256 let _ = universe.attach(sun_dir, sun);
257 universe.add(sun_dir);
258
259 // --- Desktop camera rig (for debugging while in VR) ---
260 let input = universe
261 .world
262 .add_component(InputComponent::new().with_speed(1.5));
263 let input_mode = universe.world.add_component(
264 InputTransformModeComponent::forward_z()
265 .with_fps_rotation()
266 .with_roll_axis_y(),
267 );
268 let _ = universe.attach(input, input_mode);
269
270 let desktop_rig = universe
271 .world
272 .add_component(TransformComponent::new().with_position(0.0, 1.2, 3.5));
273 let _ = universe.attach(input, desktop_rig);
274
275 let camera3d = universe.world.add_component(Camera3DComponent::new());
276 let _ = universe.attach(desktop_rig, camera3d);
277
278 let pointer = universe.world.add_component(PointerComponent::new());
279 let _ = universe.attach(camera3d, pointer);
280
281 example_util::spawn_desktop_camera_controls_hint(&mut universe, desktop_rig);
282 universe.add(input);
283
284 // --- XR rig (Aim controller debug cubes only; camera has moved to AVC) ---
285 let xr_input = universe.world.add_component(InputXRComponent::on());
286 let xr_gamepad = universe.world.add_component(
287 mittens_engine::engine::ecs::component::InputXRGamepadComponent::new().speed(1.5),
288 );
289 let xr_rig = universe.world.add_component(TransformComponent::new());
290 let _ = universe.attach(xr_input, xr_rig);
291 let _ = universe.attach(xr_input, xr_gamepad);
292
293 // renderer stats
294 let renderer_stats = universe
295 .world
296 .add_component(RendererStatsComponent::new().with_camera_target(CameraTarget::Xr));
297 let render_stats_rig = universe
298 .world
299 .add_component(TransformComponent::new().with_position(0.0, 1.85, 0.6));
300 let _ = universe.attach(render_stats_rig, renderer_stats);
301 let _ = universe.attach(xr_rig, render_stats_rig);
302
303 universe.add(xr_input);
304
305 // Background "skybox" content.
306 spawn_sun_background(&mut universe);
307
308 // --- VTuber model — single-input topology ---
309 //
310 // InputXRComponent drives body translation and head rotation through AvatarControlSystem.
311 // AvatarControlSystem:
312 // - Splices a TransformComponent under J_Bip_C_Head's parent (the neck) to drive
313 // head rotation directly. Rotating the head — not the neck — isolates the spine
314 // so the torso doesn't twist with HMD yaw.
315 // - Strips rotation from model_root (body faces body_yaw, not raw HMD yaw).
316 // - Bakes the π Y handedness correction into the head rotation math.
317 // - Smoothly rotates body to follow head when yaw delta exceeds threshold.
318 // - Measures J_Bip_C_Head local Y in the rest pose and sets model_root.y = -bone_local_y,
319 // so the head bone sits at driven_t world Y (= HMD height) with no hardcoded constant.
320 // - Re-parents CameraXRComponent under J_Bip_C_Head for first-person alignment.
321 //
322 // Topology (after AVC init):
323 // editor_root
324 // └── avatar_input_xr (InputXRComponent)
325 // └── avatar_driven_t (TransformComponent)
326 // └── AvatarControlComponent
327 // ├── body_pipeline → pipeline_output
328 // │ └── model_root (y auto-calibrated from J_Bip_C_Head)
329 // │ └── GLTFComponent → ... → J_Bip_C_Head
330 // │ └── CameraXRComponent
331 // ├── CTLXR(Left, Grip) → re-parented to lower_arm
332 // └── CTLXR(Right, Grip) → re-parented to lower_arm
333
334 let editor_root = universe.world.add_component(EditorComponent::new());
335
336 let avatar_input_xr = universe.world.add_component(InputXRComponent::on());
337 let avatar_xr_gamepad = universe.world.add_component(
338 mittens_engine::engine::ecs::component::InputXRGamepadComponent::new().speed(1.5),
339 );
340 let avatar_driven_t = universe.world.add_component(TransformComponent::new());
341 let _ = universe.attach(avatar_input_xr, avatar_driven_t);
342 let _ = universe.attach(avatar_input_xr, avatar_xr_gamepad);
343
344 // AvatarControlComponent: -Z forward (OpenXR default), body starts facing -Z (π yaw).
345 // camera_bone triggers auto-calibration of model_root.y from J_Bip_C_Head rest pose height,
346 // and causes any CameraXR/Camera3D direct children of AVC to be re-parented to that bone.
347 let avatar_control = universe.world.add_component(
348 AvatarControlComponent::new()
349 .with_head_bone("J_Bip_C_Head")
350 .with_camera_bone("J_Bip_C_Head")
351 .with_left_hand_bone("J_Bip_L_Hand")
352 .with_right_hand_bone("J_Bip_R_Hand")
353 .with_initial_yaw(std::f32::consts::PI)
354 .with_hand_rotation_smoothing(220.0),
355 );
356 let _ = universe.attach(avatar_driven_t, avatar_control);
357
358 // CameraXR as a direct child of AVC — discovered and re-parented to J_Bip_C_Head at init.
359 let camera_xr = universe.world.add_component(CameraXRComponent::on());
360 let _ = universe.attach(avatar_control, camera_xr);
361 let head_pointer = universe.world.add_component(PointerComponent::new());
362 let _ = universe.attach(camera_xr, head_pointer);
363
364 // Grip controllers for hand bone splicing — children of AvatarControlComponent so
365 // AvatarControlSystem discovers them by topology. Each needs a TransformComponent
366 // child (driven_t) that OpenXRSystem writes each tick.
367 let left_grip = universe.world.add_component(ControllerXRComponent::new(
368 true,
369 ControllerHand::Left,
370 ControllerPoseKind::Grip,
371 ));
372 let left_grip_t = universe.world.add_component(TransformComponent::new());
373 let _ = universe.attach(left_grip, left_grip_t);
374 let left_pointer = universe.world.add_component(PointerComponent::new());
375 let _ = universe.attach(left_grip_t, left_pointer);
376 let _ = universe.attach(avatar_control, left_grip);
377
378 let right_grip = universe.world.add_component(ControllerXRComponent::new(
379 true,
380 ControllerHand::Right,
381 ControllerPoseKind::Grip,
382 ));
383 let right_grip_t = universe.world.add_component(TransformComponent::new());
384 let _ = universe.attach(right_grip, right_grip_t);
385 let right_pointer = universe.world.add_component(PointerComponent::new());
386 let _ = universe.attach(right_grip_t, right_pointer);
387 let _ = universe.attach(avatar_control, right_grip);
388
389 // model_root: no explicit Y offset — AvatarControlSystem calibrates it from J_Bip_C_Head.
390 let model_root = universe.world.add_component(TransformComponent::new());
391 let model = universe
392 .world
393 .add_component(GLTFComponent::new("assets/models/pc-rei.hoodie.glb"));
394 let emissive = universe.world.add_component(EmissiveComponent::on());
395 let _ = universe.attach(model, emissive);
396
397 let _ = universe.attach(editor_root, avatar_input_xr);
398 let _ = universe.attach(avatar_control, model_root);
399 let _ = universe.attach(model_root, model);
400 universe.add(editor_root);
401
402 // --- Controller debug cubes (tracked poses) ---
403 let _left = spawn_controller_cube(
404 &mut universe,
405 xr_rig,
406 ControllerHand::Left,
407 (0.10, 0.90, 1.00, 1.0),
408 220.0,
409 options.xr_controller_rotation_filter,
410 );
411 let _right = spawn_controller_cube(
412 &mut universe,
413 xr_rig,
414 ControllerHand::Right,
415 (1.00, 0.35, 0.35, 1.0),
416 220.0,
417 options.xr_controller_rotation_filter,
418 );
419
420 // Enable OpenXR runtime.
421 let xr_root = universe.world.add_component(XrComponent::on());
422 universe.add(xr_root);
423
424 universe.systems.process_commands(
425 &mut universe.world,
426 &mut universe.visuals,
427 &mut universe.render_assets,
428 &mut universe.command_queue,
429 );
430
431 // Force the glTF subtree to spawn so we can query the armature for bone markers.
432 {
433 let systems = &mut universe.systems;
434 systems.gltf.tick_with_queue(
435 &mut universe.world,
436 &mut universe.visuals,
437 &mut systems.skinned_mesh,
438 &mut universe.command_queue,
439 0.0,
440 );
441 }
442 universe.systems.process_commands(
443 &mut universe.world,
444 &mut universe.visuals,
445 &mut universe.render_assets,
446 &mut universe.command_queue,
447 );
448
449 // Bone markers for editor inspection.
450 let marker_joints: &[(&str, (f32, f32, f32, f32))] = &[
451 ("[name='J_Bip_C_Head']", (0.85, 0.20, 0.85, 0.9)),
452 ("[name='J_Bip_C_Neck']", (0.20, 0.85, 0.85, 0.9)),
453 ("[name='J_Bip_C_UpperChest']", (0.20, 0.20, 0.85, 0.9)),
454 ("[name='J_Bip_L_UpperArm']", (0.85, 0.85, 0.20, 0.9)),
455 ("[name='J_Bip_R_UpperArm']", (0.85, 0.60, 0.20, 0.9)),
456 ];
457
458 for &(selector, color) in marker_joints {
459 let Some(bone) = universe.find_component(model_root, selector) else {
460 continue;
461 };
462 let marker_t = universe
463 .world
464 .add_component(TransformComponent::new().with_scale(0.025, 0.025, 0.025));
465 let marker_r = universe.world.add_component(RenderableComponent::cube());
466 let marker_c = universe
467 .world
468 .add_component(ColorComponent::rgba(color.0, color.1, color.2, color.3));
469 let marker_rcast = universe
470 .world
471 .add_component(RaycastableComponent::enabled());
472 let _ = universe.world.add_child(marker_r, marker_c);
473 let _ = universe.world.add_child(marker_r, marker_rcast);
474 let _ = universe.world.add_child(marker_t, marker_r);
475 let _ = universe.attach(bone, marker_t);
476 }
477
478 universe.enable_repl();
479 engine::Windowing::run_app(universe).expect("Windowing failed");
480}examples/font-example.rs (line 351)
13fn main() {
14 mittens_engine::example_support::ensure_model_assets();
15 utils::logger::init();
16
17 let world = engine::ecs::World::default();
18 let mut universe = engine::Universe::new(world);
19
20 // Dark background so the font texture pops.
21 let background = universe
22 .world
23 .add_component(BackgroundColorComponent::new());
24 let background_c = universe
25 .world
26 .add_component(ColorComponent::rgba(0.20, 0.2, 0.20, 1.0));
27 let _ = universe.world.add_child(background, background_c);
28 universe.add(background);
29
30 // Ambient so text is readable even without explicit lights.
31 let ambient = universe
32 .world
33 .add_component(AmbientLightComponent::rgb(0.50, 0.50, 0.50));
34 universe.add(ambient);
35
36 let directional_tx = universe
37 .world
38 .add_component(TransformComponent::new().with_position(0.0, 0.5, 1.0));
39 let directional_light = universe.world.add_component(
40 engine::ecs::component::DirectionalLightComponent::new()
41 .with_color(1.0, 1.0, 1.0)
42 .with_intensity(0.8),
43 );
44 let _ = universe.attach(directional_tx, directional_light);
45 universe.add(directional_tx);
46
47 // --- background clouds ---
48 // Background stage (occluded + lit) so the cloud volume self-occludes but won't occlude
49 // the foreground text (renderer clears depth before foreground).
50 let bg_root = universe
51 .world
52 .add_component(BackgroundComponent::new().with_occlusion_and_lighting());
53 universe.add(bg_root);
54
55 let mut bg_cloud_params = example_util::CloudRingParams::default();
56 bg_cloud_params.cloud_count = 6;
57 bg_cloud_params.seed = 0xF0_17_C10u32;
58 example_util::spawn_cloud_ring(&mut universe, bg_root, bg_cloud_params);
59
60 // I {
61 // // not fps rotation, just relative rotation
62 // with_forward_z()
63 // with_roll_axis_y()
64 // C3D {}
65 // }
66 let input = universe
67 .world
68 .add_component(InputComponent::new().with_speed(2.0));
69 let input_mode = universe
70 .world
71 .add_component(InputTransformModeComponent::forward_z().with_roll_axis_y());
72 let _ = universe.attach(input, input_mode);
73
74 let rig_transform = universe
75 .world
76 .add_component(TransformComponent::new().with_position(1.8, -0.5, 2.5));
77 let _ = universe.attach(input, rig_transform);
78
79 let camera = universe.world.add_component(Camera3DComponent::new());
80 let _ = universe.attach(rig_transform, camera);
81
82 // Click-to-pick: treat this camera rig as a pointer source.
83 let pointer = universe.world.add_component(PointerComponent::new());
84 let _ = universe.attach(camera, pointer);
85
86 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
87 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
88
89 universe.add(input);
90
91 // --- foreground clouds ---
92 // Normal foreground renderables (not background stage).
93 // Offset the ring forward (negative Z) so several clusters are in view.
94 let fg_cloud_root = universe
95 .world
96 .add_component(TransformComponent::new().with_position(0.0, -6.0, -10.0));
97 universe.add(fg_cloud_root);
98
99 let mut fg_cloud_params = example_util::CloudRingParams::default();
100 fg_cloud_params.cloud_count = 4;
101 fg_cloud_params.radius = 9.0;
102 fg_cloud_params.center_y = 1.0;
103 fg_cloud_params.puffs_per_cloud = 22;
104 fg_cloud_params.angle_jitter = 0.35;
105 fg_cloud_params.high_y_probability = 0.35;
106 fg_cloud_params.high_y_multiplier = 1.4;
107 fg_cloud_params.seed = 0xF0_17_C102u32;
108 example_util::spawn_cloud_ring(&mut universe, fg_cloud_root, fg_cloud_params);
109
110 // T {
111 // with_translation(0,0, -2)
112 // TXT {
113 // "ababaabbaabbaaabbbaaabbbaaaabbbbaaaaabbbbbababababa"
114 // TextureComponent { assets/images/test.font_system.png }
115 // }
116 // }
117 fn estimate_text_height_world(text: &str, scale: f32) -> f32 {
118 let line_count = text.lines().count().max(1) as f32;
119 // Text quads are ~1 unit tall per line in text-space.
120 // Add some padding so blocks don't feel cramped.
121 let pad_lines = 1.25;
122 (line_count + pad_lines) * scale
123 }
124
125 fn spawn_text_block(
126 universe: &mut engine::Universe,
127 position: (f32, f32, f32),
128 scale: f32,
129 text: &str,
130 font_texture_uri: Option<&str>,
131 text_color_rgba: Option<[f32; 4]>,
132 shadow: Option<TextShadowComponent>,
133 filtering: TextureFilteringComponent,
134 ) -> f32 {
135 // T_root { T_scale { [Color] { TXT { shadow, filtering } } } }
136 let text_root = universe.world.add_component(
137 TransformComponent::new().with_position(position.0, position.1, position.2),
138 );
139
140 let text_scale = universe
141 .world
142 .add_component(TransformComponent::new().with_scale(scale, scale, 1.0));
143 let _ = universe.attach(text_root, text_scale);
144
145 let text_parent = if let Some([r, g, b, a]) = text_color_rgba {
146 let color = universe
147 .world
148 .add_component(ColorComponent::rgba(r, g, b, a));
149 let _ = universe.attach(text_scale, color);
150 color
151 } else {
152 text_scale
153 };
154
155 let text_c = universe.world.add_component(TextComponent::new(text));
156 let _ = universe.attach(text_parent, text_c);
157
158 // Explicit opt-in: make the glyph renderables pickable.
159 // TextSystem will propagate this to all spawned glyph quads.
160 let raycastable = universe
161 .world
162 .add_component(RaycastableComponent::enabled());
163 let _ = universe.attach(text_c, raycastable);
164
165 // Route glyph quads into the alpha-to-coverage cutout pass.
166 let cutout = universe
167 .world
168 .add_component(TransparentCutoutComponent::new());
169 let _ = universe.attach(text_c, cutout);
170
171 if let Some(shadow) = shadow {
172 let shadow_id = universe.world.add_component(shadow);
173 let _ = universe.attach(text_c, shadow_id);
174 }
175
176 // Optional: override the font atlas for this text block.
177 // TextSystem will propagate this to all glyph renderables.
178 if let Some(uri) = font_texture_uri {
179 let tex = universe
180 .world
181 .add_component(TextureComponent::with_uri(uri));
182 let _ = universe.attach(text_c, tex);
183 }
184
185 let filtering_id = universe.world.add_component(filtering);
186 let _ = universe.attach(text_c, filtering_id);
187
188 universe.add(text_root);
189
190 estimate_text_height_world(text, scale)
191 }
192
193 // --- text blocks ---
194 // Multi-line samples at different scales.
195 // (Text literals omitted in the README/snippet; see constants below.)
196 const TEXT_BIG: &str = "CAT ENGINE\nfont example\nBIG TEXT";
197 const TEXT_MED: &str = "multi-line\ntext block\nmedium";
198 const TEXT_SMALL: &str = "small\ntext";
199 const TEXT_TINY: &str = "tiny\ntext\n(zoom in)";
200
201 // Stack vertically; advance by (measured height + gap) so big text gets more room.
202 let x = -1.2;
203 let z = -2.0;
204 let mut y = 1.2;
205 let gap = 0.15;
206
207 let shadow_crisp = Some(
208 TextShadowComponent::new()
209 .with_scale(1.35)
210 .with_offset([0.06, -0.06, 0.0015]),
211 );
212
213 y -= spawn_text_block(
214 &mut universe,
215 (x, y, z),
216 0.55,
217 TEXT_BIG,
218 None,
219 Some([1.0, 1.0, 1.0, 1.0]),
220 shadow_crisp,
221 TextureFilteringComponent::nearest_magnification(),
222 ) + gap;
223 y -= spawn_text_block(
224 &mut universe,
225 (x, y, z),
226 0.25,
227 TEXT_MED,
228 None,
229 Some([0.60, 0.95, 1.00, 1.0]),
230 Some(
231 TextShadowComponent::new()
232 .with_rgba([0.0, 0.0, 0.15, 1.0])
233 .with_scale(1.20)
234 .with_offset([0.05, -0.04, 0.0015]),
235 ),
236 TextureFilteringComponent::linear(),
237 ) + gap;
238 y -= spawn_text_block(
239 &mut universe,
240 (x, y, z),
241 0.14,
242 TEXT_SMALL,
243 None,
244 Some([1.0, 0.88, 0.35, 1.0]),
245 Some(
246 TextShadowComponent::new()
247 .with_rgba([0.15, 0.0, 0.0, 1.0])
248 .with_scale(1.55)
249 .with_offset([0.08, -0.08, 0.0015]),
250 ),
251 TextureFilteringComponent::nearest(),
252 ) + gap;
253 let _ = spawn_text_block(
254 &mut universe,
255 (x, y, z),
256 0.08,
257 TEXT_TINY,
258 None,
259 Some([0.90, 1.0, 0.70, 1.0]),
260 shadow_crisp,
261 TextureFilteringComponent::nearest_magnification(),
262 );
263
264 // Left block: explicit multi-line text using the default font_system atlas.
265 const TEXT_LEFT: &str = "even though there's hexes\nto the solar plexus in my lexus\ni'm feelin' reckless,\nwhen i'm eating breakfast";
266 let _ = spawn_text_block(
267 &mut universe,
268 (x - 8.1, 1.1, z),
269 0.22,
270 TEXT_LEFT,
271 Some("assets/textures/font_system.dds"),
272 Some([0.95, 0.95, 0.95, 1.0]),
273 Some(
274 TextShadowComponent::new()
275 .with_scale(1.25)
276 .with_offset([0.05, -0.05, 0.0015]),
277 ),
278 TextureFilteringComponent::nearest_magnification(),
279 );
280
281 // Alt atlas: put it *behind* the original stack (slightly farther from the camera)
282 // and tint it dark grey.
283 let alt_atlas = Some("assets/textures/font_system.0.0.dds");
284 let alt_z = z - 0.05;
285 let alt_grey = Some([0.25, 0.25, 0.25, 1.0]);
286
287 let mut y_alt = 1.2;
288 y_alt -= spawn_text_block(
289 &mut universe,
290 (x, y_alt, alt_z),
291 0.55,
292 TEXT_BIG,
293 alt_atlas,
294 alt_grey,
295 Some(
296 TextShadowComponent::new()
297 .with_rgba([0.0, 0.0, 0.0, 1.0])
298 .with_scale(1.15)
299 .with_offset([0.03, -0.03, 0.0015]),
300 ),
301 TextureFilteringComponent::linear(),
302 ) + gap;
303 y_alt -= spawn_text_block(
304 &mut universe,
305 (x, y_alt, alt_z),
306 0.25,
307 TEXT_MED,
308 alt_atlas,
309 alt_grey,
310 Some(
311 TextShadowComponent::new()
312 .with_rgba([0.0, 0.0, 0.0, 1.0])
313 .with_scale(1.15)
314 .with_offset([0.03, -0.03, 0.0015]),
315 ),
316 TextureFilteringComponent::linear(),
317 ) + gap;
318 y_alt -= spawn_text_block(
319 &mut universe,
320 (x, y_alt, alt_z),
321 0.14,
322 TEXT_SMALL,
323 alt_atlas,
324 alt_grey,
325 Some(
326 TextShadowComponent::new()
327 .with_rgba([0.0, 0.0, 0.0, 1.0])
328 .with_scale(1.15)
329 .with_offset([0.03, -0.03, 0.0015]),
330 ),
331 TextureFilteringComponent::linear(),
332 ) + gap;
333 let _ = spawn_text_block(
334 &mut universe,
335 (x, y_alt, alt_z),
336 0.08,
337 TEXT_TINY,
338 alt_atlas,
339 alt_grey,
340 Some(
341 TextShadowComponent::new()
342 .with_rgba([0.0, 0.0, 0.0, 1.0])
343 .with_scale(1.15)
344 .with_offset([0.03, -0.03, 0.0015]),
345 ),
346 TextureFilteringComponent::linear(),
347 );
348
349 universe.enable_repl();
350
351 let xr_input = universe.world.add_component(InputXRComponent::on());
352 let xr_gamepad = universe
353 .world
354 .add_component(engine::ecs::component::InputXRGamepadComponent::new().speed(1.5));
355 let xr_head = universe.world.add_component(TransformComponent::new());
356 let xr_camera = universe.world.add_component(CameraXRComponent::on());
357 let _ = universe.attach(xr_input, xr_head);
358 let _ = universe.attach(xr_input, xr_gamepad);
359 let _ = universe.attach(xr_head, xr_camera);
360 universe.add(xr_input);
361
362 // Add an OpenXR component so OpenXRSystem initializes and starts polling events.
363 let xr_root = universe
364 .world
365 .add_component(engine::ecs::component::XrComponent::on());
366 universe.add(xr_root);
367
368 // Process init-time registrations (Text expands into glyph subtrees here).
369 universe.systems.process_commands(
370 &mut universe.world,
371 &mut universe.visuals,
372 &mut universe.render_assets,
373 &mut universe.command_queue,
374 );
375
376 engine::Windowing::run_app(universe).expect("Windowing failed");
377}examples/vtuber-joints-example.rs (line 111)
14fn main() {
15 mittens_engine::example_support::ensure_model_assets();
16 utils::logger::init();
17
18 let world = engine::ecs::World::default();
19 let mut universe = engine::Universe::new(world);
20
21 // Slow the global beat clock so beat-based animations run half as fast.
22 let clock = universe
23 .world
24 .add_component(ClockComponent::new().with_bpm(60.0));
25 universe.add(clock);
26
27 // Light pink background.
28 let background = universe
29 .world
30 .add_component(BackgroundColorComponent::new());
31 let background_c = universe
32 .world
33 .add_component(ColorComponent::rgba(1.0, 0.82, 0.90, 1.0));
34 let _ = universe.world.add_child(background, background_c);
35 universe.add(background);
36
37 // Small ambient so shadowed areas aren't pure black.
38 let ambient = universe
39 .world
40 .add_component(AmbientLightComponent::rgb(0.10, 0.10, 0.12));
41 universe.add(ambient);
42
43 // --- Camera rig (WASD + mouse) ---
44 let input = universe
45 .world
46 .add_component(InputComponent::new().with_speed(1.5));
47 let input_mode = universe.world.add_component(
48 InputTransformModeComponent::forward_z()
49 .with_fps_rotation()
50 .with_roll_axis_y(),
51 );
52 let _ = universe.attach(input, input_mode);
53
54 // Start slightly pulled back looking towards the origin.
55 let rig_transform = universe
56 .world
57 .add_component(TransformComponent::new().with_position(0.0, 0.0, 6.0));
58 let _ = universe.attach(input, rig_transform);
59
60 let camera3d = universe.world.add_component(Camera3DComponent::new());
61 let _ = universe.attach(rig_transform, camera3d);
62
63 // Pointer so gizmos can be interacted with.
64 let pointer = universe.world.add_component(PointerComponent::new());
65 let _ = universe.attach(camera3d, pointer);
66
67 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
68 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
69
70 universe.add(input);
71
72 // --- lighting ---
73 let sun = universe.world.add_component(
74 DirectionalLightComponent::new()
75 .with_intensity(1.2)
76 .with_color(1.0, 0.98, 0.94),
77 );
78 let sun_dir = universe
79 .world
80 .add_component(TransformComponent::new().with_position(0.0, -0.35, 1.0));
81 let _ = universe.attach(sun_dir, sun);
82 universe.add(sun_dir);
83
84 let light_transform = universe.world.add_component(
85 TransformComponent::new()
86 .with_position(1.0, 6.0, 3.0)
87 .with_scale(0.1, 0.1, 0.1),
88 );
89 let light = universe.world.add_component(
90 engine::ecs::component::PointLightComponent::new()
91 .with_distance(120.0)
92 .with_color(1.0, 1.0, 1.0),
93 );
94 let _ = universe.attach(light_transform, light);
95 universe.add(light_transform);
96
97 // --- VTuber model ---
98 let model_uri = "assets/models/pc-rei.hoodie.glb";
99
100 // Wrap the model subtree in an editor root so transform-only glTF nodes can be visualized
101 // (and thus raycasted/selected) without affecting non-editor scenes.
102 let editor_root = universe.world.add_component(EditorComponent::new());
103
104 let model_root = universe.world.add_component(TransformComponent::new());
105 let model = universe.world.add_component(GLTFComponent::new(model_uri));
106
107 // emissive for pc-rei
108 let emissive = universe.world.add_component(EmissiveComponent::on());
109 let _ = universe.attach(model, emissive);
110
111 let xr_input = universe.world.add_component(InputXRComponent::on());
112 let xr_gamepad = universe
113 .world
114 .add_component(engine::ecs::component::InputXRGamepadComponent::new().speed(1.5));
115 let xr_head = universe.world.add_component(TransformComponent::new());
116 let xr_camera = universe.world.add_component(CameraXRComponent::on());
117 let _ = universe.attach(xr_input, xr_head);
118 let _ = universe.attach(xr_input, xr_gamepad);
119 let _ = universe.attach(xr_head, xr_camera);
120 let xr_pointer = universe.world.add_component(PointerComponent::new());
121 let _ = universe.attach(xr_camera, xr_pointer);
122 let _ = universe.attach(xr_head, editor_root);
123
124 let _ = universe.attach(model_root, model);
125
126 let _ = universe.attach(editor_root, model_root);
127
128 // Initialize the editor root so GLTFComponent gets registered.
129 universe.add(xr_input);
130 universe.add(editor_root);
131
132 // --- Background clouds (occluded + lit) ---
133 let bg_root = universe.world.add_component(
134 engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
135 );
136 universe.add(bg_root);
137 let mut cloud_params = example_util::CloudRingParams::default();
138 cloud_params.cloud_count = 8; // +3 clusters
139 cloud_params.angle_jitter = 0.35;
140 cloud_params.high_y_probability = 0.5;
141 cloud_params.high_y_multiplier = 1.5;
142 cloud_params.seed = 0x57_55_B0_01u32;
143 example_util::spawn_cloud_ring(&mut universe, bg_root, cloud_params);
144
145 // --- Simple environment ---
146 let spawn_cube = |universe: &mut engine::Universe,
147 position: (f32, f32, f32),
148 scale: (f32, f32, f32),
149 color: (f32, f32, f32, f32)| {
150 let transform = universe.world.add_component(
151 TransformComponent::new()
152 .with_position(position.0, position.1, position.2)
153 .with_scale(scale.0, scale.1, scale.2),
154 );
155 let renderable = universe.world.add_component(RenderableComponent::cube());
156 let color = universe
157 .world
158 .add_component(ColorComponent::rgba(color.0, color.1, color.2, color.3));
159
160 let _ = universe.attach(transform, renderable);
161 let _ = universe.attach(renderable, color);
162
163 universe.add(transform);
164 };
165
166 // floor
167 spawn_cube(
168 &mut universe,
169 (0.0, -0.05, 0.0),
170 (10.0, 0.1, 10.0),
171 (0.92, 0.92, 0.92, 1.0),
172 );
173
174 // back wall
175 spawn_cube(
176 &mut universe,
177 (-3.0, 1.5, -5.0),
178 (3.0, 3.0, 1.0),
179 (0.95, 0.94, 0.96, 1.0),
180 );
181
182 // desk
183 spawn_cube(
184 &mut universe,
185 (0.0, 0.35, 1.0),
186 (1.0, 0.75, 0.5),
187 (0.75, 0.70, 0.65, 1.0),
188 );
189
190 // --- Editor-side stacked cubes (inside the editor subtree for picking/gizmos) ---
191 {
192 let spawn_editor_cube = |universe: &mut engine::Universe,
193 editor_root: engine::ecs::ComponentId,
194 name: &str,
195 position: (f32, f32, f32),
196 scale: (f32, f32, f32),
197 color: (f32, f32, f32, f32)| {
198 let transform = universe.world.add_component_boxed_named(
199 format!("{name}_t"),
200 Box::new(
201 TransformComponent::new()
202 .with_position(position.0, position.1, position.2)
203 .with_scale(scale.0, scale.1, scale.2),
204 ),
205 );
206 let renderable = universe.world.add_component_boxed_named(
207 format!("{name}_r"),
208 Box::new(RenderableComponent::cube()),
209 );
210 let color_comp = universe.world.add_component_boxed_named(
211 format!("{name}_color"),
212 Box::new(ColorComponent::rgba(color.0, color.1, color.2, color.3)),
213 );
214 let raycastable = universe.world.add_component_boxed_named(
215 format!("{name}_raycastable"),
216 Box::new(RaycastableComponent::enabled()),
217 );
218
219 let _ = universe.world.add_child(transform, renderable);
220 let _ = universe.world.add_child(renderable, color_comp);
221 let _ = universe.world.add_child(renderable, raycastable);
222
223 // One attach into the initialized editor subtree triggers init for the new subtree.
224 let _ = universe.attach(editor_root, transform);
225 };
226
227 // Place the stack beside the desk (a bit to the right).
228 let stack_x = 1.35;
229 let stack_z = 1.0;
230 let s = 0.25;
231 let half = 0.5 * s;
232 let light_brown = (0.80, 0.72, 0.55, 1.0);
233 let cyan = (0.20, 1.00, 1.00, 1.0);
234
235 spawn_editor_cube(
236 &mut universe,
237 editor_root,
238 "editor_stack_0",
239 (stack_x, half, stack_z),
240 (s, s, s),
241 light_brown,
242 );
243 spawn_editor_cube(
244 &mut universe,
245 editor_root,
246 "editor_stack_1",
247 (stack_x, half + 1.0 * s, stack_z),
248 (s, s, s),
249 light_brown,
250 );
251 spawn_editor_cube(
252 &mut universe,
253 editor_root,
254 "editor_stack_2",
255 (stack_x, half + 2.0 * s, stack_z),
256 (s, s, s),
257 light_brown,
258 );
259 spawn_editor_cube(
260 &mut universe,
261 editor_root,
262 "editor_stack_top",
263 (stack_x, half + 3.0 * s, stack_z),
264 (s, s, s),
265 cyan,
266 );
267 }
268
269 let xr_root = universe
270 .world
271 .add_component(engine::ecs::component::XrComponent::on());
272 universe.add(xr_root);
273
274 universe.systems.process_commands(
275 &mut universe.world,
276 &mut universe.visuals,
277 &mut universe.render_assets,
278 &mut universe.command_queue,
279 );
280
281 // Spawn the glTF subtree once up-front so joint ComponentIds exist.
282 {
283 let systems = &mut universe.systems;
284 systems.gltf.tick_with_queue(
285 &mut universe.world,
286 &mut universe.visuals,
287 &mut systems.skinned_mesh,
288 &mut universe.command_queue,
289 0.0,
290 );
291 }
292 universe.systems.process_commands(
293 &mut universe.world,
294 &mut universe.visuals,
295 &mut universe.render_assets,
296 &mut universe.command_queue,
297 );
298
299 // Register imported meshes into RenderAssets early so we can inspect skin weights
300 // (textures will still be uploaded later during normal rendering).
301 universe
302 .systems
303 .gltf
304 .flush_mesh_imports_only(&mut universe.render_assets);
305
306 // --- Joint printout + animation binding (in the example) ---
307 let all_joints = collect_joint_transforms(
308 &universe.world,
309 &universe.systems.skinned_mesh,
310 &universe.visuals,
311 model,
312 model_root,
313 );
314 println!("[vtuber-joints-example] joints found: {}", all_joints.len());
315 for (i, (node_index, joint_tx)) in all_joints.iter().enumerate() {
316 println!(" joint[{i:03}] node_index={node_index} transform={joint_tx:?}");
317 }
318
319 let node_index_to_transform: HashMap<usize, engine::ecs::ComponentId> =
320 all_joints.iter().copied().collect();
321
322 // Example settings are hardcoded to keep this example simple.
323 let joint_offset: usize = 0;
324 let wiggle_count: usize = 16;
325
326 let target_mesh_key = "pc-rei.hoodie:Body_(merged).baked:prim0".to_string();
327 let target_joint_names: Vec<String> = vec![
328 "J_Bip_L_UpperArm".to_string(),
329 "J_Bip_R_UpperArm".to_string(),
330 ];
331
332 let print_transform_updates: bool = false;
333
334 let selected_joint_transforms: Vec<(usize, engine::ecs::ComponentId)> =
335 select_named_joints(&universe.world, &all_joints, &target_joint_names)
336 .or_else(|| {
337 select_body_prim0_influencers(
338 &universe,
339 model_root,
340 &target_mesh_key,
341 wiggle_count,
342 &node_index_to_transform,
343 )
344 })
345 .unwrap_or_else(|| select_joint_range(&all_joints, joint_offset, wiggle_count));
346 println!(
347 "[vtuber-joints-example] wiggle selection: target_mesh_key='{}' offset={} count={} selected={}",
348 target_mesh_key,
349 joint_offset,
350 wiggle_count,
351 selected_joint_transforms.len()
352 );
353
354 debug_print_selected_joint_influence(
355 &universe,
356 &target_mesh_key,
357 model_root,
358 &selected_joint_transforms,
359 );
360
361 println!("[vtuber-joints-example] selected joints:");
362 for (i, (node_index, joint_tx)) in selected_joint_transforms.iter().enumerate() {
363 let name = universe
364 .world
365 .get_component_record(*joint_tx)
366 .map(|n| n.name.as_str())
367 .unwrap_or("<unknown>");
368 println!(" sel[{i:02}] node_index={node_index} name={name} transform={joint_tx:?}");
369 }
370
371 if print_transform_updates {
372 println!("[vtuber-joints-example] note: joint animation disabled");
373 }
374
375 universe.systems.process_commands(
376 &mut universe.world,
377 &mut universe.visuals,
378 &mut universe.render_assets,
379 &mut universe.command_queue,
380 );
381
382 universe.enable_repl();
383 engine::Windowing::run_app(universe).expect("Windowing failed");
384}pub fn off() -> Self
Trait Implementations§
Source§impl Clone for InputXRComponent
impl Clone for InputXRComponent
Source§fn clone(&self) -> InputXRComponent
fn clone(&self) -> InputXRComponent
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 InputXRComponent
impl Component for InputXRComponent
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
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§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
Source§impl Debug for InputXRComponent
impl Debug for InputXRComponent
Auto Trait Implementations§
impl Freeze for InputXRComponent
impl RefUnwindSafe for InputXRComponent
impl Send for InputXRComponent
impl Sync for InputXRComponent
impl Unpin for InputXRComponent
impl UnsafeUnpin for InputXRComponent
impl UnwindSafe for InputXRComponent
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.