1use mittens_engine::engine::ecs::component::{
2 AmbientLightComponent, AvatarControlComponent, BackgroundColorComponent, BloomComponent,
3 BlurPassComponent, Camera3DComponent, CameraXRComponent, ColorComponent, ControllerHand,
4 ControllerPoseKind, ControllerXRComponent, DirectionalLightComponent, EditorComponent,
5 EmissiveComponent, EmissivePassComponent, GLTFComponent, InputComponent,
6 InputTransformModeComponent, InputXRComponent, PointerComponent, QuatTemporalFilterComponent,
7 RaycastableComponent, RenderGraphComponent, RenderableComponent, RendererSettingsComponent,
8 RendererStatsComponent, TransformComponent, TransformForkTRSComponent,
9 TransformMapRotationComponent, TransformMapScaleComponent, TransformMapTranslationComponent,
10 XrComponent,
11};
12use mittens_engine::engine::graphics::BuiltinMeshType;
13use mittens_engine::engine::graphics::CameraTarget;
14use mittens_engine::engine::graphics::primitives::{MaterialHandle, Renderable};
15use mittens_engine::{engine, utils};
16
17#[path = "example_util/mod.rs"]
18mod example_util;
19
20#[derive(Debug, Clone, Copy)]
21struct VrInputOptions {
22 xr_controller_rotation_filter: bool,
23}
24
25impl Default for VrInputOptions {
26 fn default() -> Self {
27 Self {
28 xr_controller_rotation_filter: true,
29 }
30 }
31}
32
33fn parse_options() -> Result<VrInputOptions, String> {
34 let mut options = VrInputOptions::default();
35 let mut args = std::env::args().skip(1);
36
37 while let Some(arg) = args.next() {
38 match arg.as_str() {
39 "--xr-controller-rotation-filter" => {
40 options.xr_controller_rotation_filter = true;
41 }
42 "--no-xr-controller-rotation-filter" => {
43 options.xr_controller_rotation_filter = false;
44 }
45 "--help" | "-h" => {
46 return Err(
47 "usage: cargo run --example vr-input -- [--xr-controller-rotation-filter | --no-xr-controller-rotation-filter]"
48 .to_string(),
49 );
50 }
51 other => {
52 return Err(format!(
53 "unknown arg: {other}\nusage: cargo run --example vr-input -- [--xr-controller-rotation-filter | --no-xr-controller-rotation-filter]"
54 ));
55 }
56 }
57 }
58
59 Ok(options)
60}
61
62fn spawn_sun_background(universe: &mut engine::Universe) {
63 let bg_root = universe
64 .world
65 .add_component(engine::ecs::component::BackgroundComponent::new());
66 universe.add(bg_root);
67
68 let circle_mesh = universe.render_assets.get_mesh(BuiltinMeshType::Circle2D);
69
70 let sun_t = universe.world.add_component(
72 TransformComponent::new()
73 .with_position(2.0, 1.5, -8.0)
74 .with_scale(3.5, 3.5, 3.5),
75 );
76 let sun_r = universe
77 .world
78 .add_component(RenderableComponent::new(Renderable::new(
79 circle_mesh,
80 MaterialHandle::TOON_MESH,
81 )));
82 let sun_color = universe
83 .world
84 .add_component(ColorComponent::rgba(1.0, 0.85, 0.15, 1.0));
85 let sun_emissive = universe.world.add_component(EmissiveComponent::on());
86
87 let _ = universe.attach(bg_root, sun_t);
88 let _ = universe.attach(sun_t, sun_r);
89 let _ = universe.attach(sun_r, sun_color);
90 let _ = universe.attach(sun_r, sun_emissive);
91
92 let highlight_t = universe.world.add_component(
94 TransformComponent::new()
95 .with_position(-0.35, 0.35, -0.01)
96 .with_scale(0.45, 0.45, 0.45),
97 );
98 let highlight_r = universe
99 .world
100 .add_component(RenderableComponent::new(Renderable::new(
101 circle_mesh,
102 MaterialHandle::TOON_MESH,
103 )));
104 let highlight_color = universe
105 .world
106 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
107 let highlight_emissive = universe.world.add_component(EmissiveComponent::on());
108
109 let _ = universe.attach(sun_t, highlight_t);
110 let _ = universe.attach(highlight_t, highlight_r);
111 let _ = universe.attach(highlight_r, highlight_color);
112 let _ = universe.attach(highlight_r, highlight_emissive);
113}
114
115fn spawn_controller_cube(
116 universe: &mut engine::Universe,
117 xr_rig: engine::ecs::ComponentId,
118 hand: ControllerHand,
119 color: (f32, f32, f32, f32),
120 rotation_smoothing: f32,
121 use_rotation_filter_pipeline: bool,
122) -> engine::ecs::ComponentId {
123 let controller_marker = universe.world.add_component(ControllerXRComponent::new(
124 true,
125 hand,
126 ControllerPoseKind::Aim,
127 ));
128 let _ = universe.attach(xr_rig, controller_marker);
129
130 let controller_t = universe
132 .world
133 .add_component(TransformComponent::new().with_scale(0.06, 0.06, 0.12));
134 let _ = universe.attach(controller_marker, controller_t);
135
136 if !use_rotation_filter_pipeline {
137 let cube = universe.world.add_component(RenderableComponent::cube());
138 let cube_color = universe
139 .world
140 .add_component(ColorComponent::rgba(color.0, color.1, color.2, color.3));
141
142 let _ = universe.attach(controller_t, cube);
143 let _ = universe.attach(cube, cube_color);
144
145 return controller_marker;
146 }
147
148 let fork = universe
149 .world
150 .add_component(TransformForkTRSComponent::new());
151 let _ = universe.attach(controller_t, fork);
152
153 let map_translation = universe
154 .world
155 .add_component(TransformMapTranslationComponent::new());
156 let _ = universe.attach(fork, map_translation);
157
158 let map_rotation = universe
159 .world
160 .add_component(TransformMapRotationComponent::new());
161 let _ = universe.attach(fork, map_rotation);
162 let rotation_filter = universe.world.add_component(
163 QuatTemporalFilterComponent::new().with_smoothing_factor(rotation_smoothing),
164 );
165 let _ = universe.attach(map_rotation, rotation_filter);
166
167 let map_scale = universe
168 .world
169 .add_component(TransformMapScaleComponent::new());
170 let _ = universe.attach(fork, map_scale);
171
172 let filtered_t = universe.world.add_component(TransformComponent::new());
173 let _ = universe.attach(fork, filtered_t);
174
175 let cube = universe.world.add_component(RenderableComponent::cube());
176 let cube_color = universe
177 .world
178 .add_component(ColorComponent::rgba(color.0, color.1, color.2, color.3));
179
180 let _ = universe.attach(filtered_t, cube);
181 let _ = universe.attach(cube, cube_color);
182
183 controller_marker
184}
185
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 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 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 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 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 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 spawn_sun_background(&mut universe);
307
308 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 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 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 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 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 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 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 {
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 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}