1use crate::engine::ecs::component::{
2 AvatarControlComponent, BoneRestPoseComponent, Camera3DComponent, CameraXRComponent,
3 ControllerHand, ControllerXRComponent, IKChainComponent, IKSolver, QuatYawFollowComponent,
4 SerializeComponent, TransformComponent, TransformForkTRSComponent,
5 TransformMapRotationComponent,
6};
7use crate::engine::ecs::{ComponentId, IntentValue, SignalEmitter, World};
8use crate::engine::user_input::InputState;
9use crate::utils::math::{
10 mat_to_quat, quat_conjugate, quat_mul, quat_rotate_vec3, quat_rotation_y,
11};
12use std::collections::HashSet;
13use winit::keyboard::{Key, NamedKey};
14
15#[derive(Debug, Default)]
16pub struct AvatarControlSystem {
17 avatars: HashSet<ComponentId>,
18}
19
20impl AvatarControlSystem {
21 pub fn new() -> Self {
22 Self::default()
23 }
24
25 pub fn tick(
26 &mut self,
27 world: &mut World,
28 input: &InputState,
29 emit: &mut dyn SignalEmitter,
30 dt_sec: f32,
31 ) {
32 let ids: Vec<_> = self.avatars.iter().copied().collect();
33
34 let mut calibration_consumed = false;
35 let calibrate_pressed = input.key_pressed(&Key::Named(NamedKey::Enter));
36 for id in ids {
37 let allow_calibration = calibrate_pressed && !calibration_consumed;
38 if tick_one(id, world, emit, dt_sec, allow_calibration) {
39 calibration_consumed = true;
40 }
41 }
42 }
43
44 pub fn register(&mut self, component: ComponentId) {
45 self.avatars.insert(component);
46 }
47
48 pub fn remove(&mut self, component: ComponentId) {
49 self.avatars.remove(&component);
50 }
51}
52
53fn tick_one(
54 id: ComponentId,
55 world: &mut World,
56 emit: &mut dyn SignalEmitter,
57 _dt_sec: f32,
58 allow_calibration: bool,
59) -> bool {
60 let needs_init = {
62 let Some(c) = world.get_component_by_id_as::<AvatarControlComponent>(id) else {
63 return false;
64 };
65 c.splice_head.is_none()
66 };
67
68 if needs_init {
69 if !ancestor_input_xr_is_ready(world, id) {
74 return false;
75 }
76 try_init_splices(id, world, emit);
77 }
79
80 let displaced_head_id = world
84 .get_component_by_id_as::<AvatarControlComponent>(id)
85 .and_then(|c| c.displaced_head);
86 if let Some(head_bone_id) = displaced_head_id {
87 if let Some(head_t) = world.get_component_by_id_as::<TransformComponent>(head_bone_id) {
88 if head_t.transform.translation != [0.0, 0.0, 0.0] {
89 emit.push_intent_now(
90 head_bone_id,
91 IntentValue::UpdateTransform {
92 component_ids: vec![head_bone_id],
93 translation: [0.0, 0.0, 0.0],
94 rotation_quat_xyzw: head_t.transform.rotation,
95 scale: head_t.transform.scale,
96 },
97 );
98 }
99 }
100 }
101
102 if allow_calibration && capture_hand_grip_calibration(id, world, emit) {
103 return true;
104 }
105 false
106}
107
108fn ancestor_input_xr_is_ready(world: &World, start: ComponentId) -> bool {
109 let mut current = Some(start);
110 while let Some(component) = current {
111 if let Some(input) = world
112 .get_component_by_id_as::<crate::engine::ecs::component::InputXRComponent>(component)
113 {
114 return input.pose_valid;
115 }
116 current = world.parent_of(component);
117 }
118 true
119}
120
121fn try_init_splices(id: ComponentId, world: &mut World, emit: &mut dyn SignalEmitter) {
130 let (
131 head_bone_name,
132 left_hand_bone,
133 right_hand_bone,
134 left_upper_arm_bone,
135 left_lower_arm_bone,
136 right_upper_arm_bone,
137 right_lower_arm_bone,
138 left_arm_pole_direction,
139 right_arm_pole_direction,
140 body_yaw_threshold,
141 body_yaw_rate,
142 authored_forward_plus_z,
143 forward_plus_z_overridden,
144 authored_initial_body_yaw,
145 initial_body_yaw_overridden,
146 skip_body_pipeline,
147 camera_bone_name,
148 avatar_height_override,
149 eye_height_from_head_bone,
150 head_ik_eye_height,
151 neck_bone_name,
152 hand_grip_rotation_left,
153 hand_grip_rotation_right,
154 ) = {
155 let Some(c) = world.get_component_by_id_as::<AvatarControlComponent>(id) else {
156 return;
157 };
158 (
159 c.head_bone.clone(),
160 c.left_hand_bone.clone(),
161 c.right_hand_bone.clone(),
162 c.left_upper_arm_bone.clone(),
163 c.left_lower_arm_bone.clone(),
164 c.right_upper_arm_bone.clone(),
165 c.right_lower_arm_bone.clone(),
166 c.left_arm_pole_direction,
167 c.right_arm_pole_direction,
168 c.body_yaw_threshold,
169 c.body_yaw_rate,
170 c.forward_plus_z,
171 c.forward_plus_z_overridden,
172 c.initial_body_yaw,
173 c.initial_body_yaw_overridden,
174 c.skip_body_pipeline,
175 c.camera_bone.clone(),
176 c.avatar_height,
177 c.eye_height_from_head_bone,
178 c.head_ik_eye_height,
179 c.neck_bone.clone(),
180 c.hand_grip_rotation_left,
181 c.hand_grip_rotation_right,
182 )
183 };
184
185 let Some(model_root_id) = world.children_of(id).iter().copied().find(|&ch| {
187 world
188 .get_component_by_id_as::<TransformComponent>(ch)
189 .is_some()
190 }) else {
191 return;
192 };
193
194 let left_ctrl = world.children_of(id).iter().copied().find(|&ch| {
196 world
197 .get_component_by_id_as::<ControllerXRComponent>(ch)
198 .map(|c| c.hand == ControllerHand::Left)
199 .unwrap_or(false)
200 });
201 let right_ctrl = world.children_of(id).iter().copied().find(|&ch| {
202 world
203 .get_component_by_id_as::<ControllerXRComponent>(ch)
204 .map(|c| c.hand == ControllerHand::Right)
205 .unwrap_or(false)
206 });
207
208 let Some(driven_t_id) = world.parent_of(id) else {
210 return;
211 };
212 let resolved_body_forward_plus_z = if forward_plus_z_overridden {
213 authored_forward_plus_z
214 } else {
215 false
216 };
217 let resolved_head_target_forward_plus_z = if forward_plus_z_overridden {
218 authored_forward_plus_z
219 } else {
220 false
221 };
222 let resolved_initial_body_yaw = if initial_body_yaw_overridden {
223 authored_initial_body_yaw
224 } else {
225 std::f32::consts::PI
226 };
227
228 let head_selector = format!("#{}", head_bone_name);
230 let Some(head_bone_id) = world.find_component(model_root_id, &head_selector) else {
231 return;
232 };
233 let Some(head_parent_id) = world.parent_of(head_bone_id) else {
234 return;
235 };
236
237 let (head_rest_t, head_rest_rot, head_rest_s) = read_bone_rest_pose(world, head_bone_id);
245 let head_splice_id = world.add_component(TransformComponent::new().with_position(
246 head_rest_t[0],
247 head_rest_t[1],
248 head_rest_t[2],
249 ));
250
251 let left = resolve_hand_splice(
256 world,
257 model_root_id,
258 left_hand_bone.as_deref(),
259 left_ctrl,
260 hand_grip_rotation_left,
261 );
262 let right = resolve_hand_splice(
263 world,
264 model_root_id,
265 right_hand_bone.as_deref(),
266 right_ctrl,
267 hand_grip_rotation_right,
268 );
269
270 let actual_camera_bone_name = camera_bone_name.as_deref().or(Some(&head_bone_name));
280 let camera_bone_id: Option<ComponentId> = actual_camera_bone_name.and_then(|name| {
281 let sel = format!("#{}", name);
282 let found = world.find_component(model_root_id, &sel);
283 found
284 });
285
286 let camera_children: Vec<(ComponentId, [f32; 3], bool)> = world
290 .children_of(id)
291 .iter()
292 .copied()
293 .filter_map(|ch| {
294 let is_c3d = world
295 .get_component_by_id_as::<Camera3DComponent>(ch)
296 .is_some();
297 let is_cxr = world
298 .get_component_by_id_as::<CameraXRComponent>(ch)
299 .is_some();
300 if is_c3d || is_cxr {
301 return Some((ch, [0.0, 0.0, 0.0], is_c3d));
302 }
303 if let Some(tc) = world.get_component_by_id_as::<TransformComponent>(ch) {
304 let wraps_c3d = world.children_of(ch).iter().any(|&gc| {
305 world
306 .get_component_by_id_as::<Camera3DComponent>(gc)
307 .is_some()
308 });
309 let wraps_cxr = world.children_of(ch).iter().any(|&gc| {
310 world
311 .get_component_by_id_as::<CameraXRComponent>(gc)
312 .is_some()
313 });
314 let wraps_cam = wraps_c3d || wraps_cxr;
315 if wraps_cam {
316 let eye_offset = tc.transform.translation;
317 return Some((ch, eye_offset, wraps_c3d));
318 }
319 }
320 None
321 })
322 .collect();
323 let eye_offset_head_local: [f32; 3] = camera_children
324 .iter()
325 .map(|&(_, off, _)| off)
326 .find(|off| off != &[0.0, 0.0, 0.0])
327 .unwrap_or([0.0, eye_height_from_head_bone.unwrap_or(0.0), 0.0]);
328
329 let head_ik_offset_yaw = if resolved_head_target_forward_plus_z {
334 0.0
335 } else {
336 std::f32::consts::PI
337 };
338
339 let model_root_translation: Option<[f32; 3]> = if let Some(h) = avatar_height_override {
345 Some([0.0, -h, 0.0])
346 } else if let Some(cam_bone_id) = camera_bone_id {
347 let cam_bone_world_y = world
348 .get_component_by_id_as::<TransformComponent>(cam_bone_id)
349 .map(|t| t.transform.matrix_world[3][1])
350 .unwrap_or(0.0);
351 let model_root_world_y = world
352 .get_component_by_id_as::<TransformComponent>(model_root_id)
353 .map(|t| t.transform.matrix_world[3][1])
354 .unwrap_or(0.0);
355 let bone_local_y = cam_bone_world_y - model_root_world_y;
356 Some([0.0, -bone_local_y, 0.0])
357 } else {
358 None
359 };
360
361 if let Some(txyz) = model_root_translation {
366 emit.push_intent_now(
367 model_root_id,
368 IntentValue::UpdateTransform {
369 component_ids: vec![model_root_id],
370 translation: txyz,
371 rotation_quat_xyzw: [0.0, 0.0, 0.0, 1.0],
372 scale: [1.0, 1.0, 1.0],
373 },
374 );
375 }
376
377 let (neck_bone_id, neck_rest_t) = match neck_bone_name.as_deref() {
382 Some(name) => {
383 let sel = format!("#{}", name);
384 match world.find_component(model_root_id, &sel) {
385 Some(nid) => {
386 let (rest_t, _, _) = read_bone_rest_pose(world, nid);
387 (Some(nid), Some(rest_t))
388 }
389 None => {
390 println!(
391 "[AVC] neck bone '{}' not found under model_root — neck pin disabled",
392 name
393 );
394 (None, None)
395 }
396 }
397 }
398 None => (None, None),
399 };
400
401 let model_root_local_y = model_root_translation.map(|t| t[1]).unwrap_or(0.0);
406
407 if let Some(c) = world.get_component_by_id_as_mut::<AvatarControlComponent>(id) {
409 c.splice_head = Some(head_splice_id);
410 c.displaced_head = Some(head_bone_id);
411 c.splice_camera_bone = camera_bone_id;
412 c.model_root_id = Some(model_root_id);
413 c.model_root_local_y = model_root_local_y;
414 c.neck_bone_id = neck_bone_id;
415 c.neck_rest_translation = neck_rest_t;
416 if let Some((_, _, _, bone)) = left {
417 c.left_hand_bone_id = Some(bone);
418 }
419 if let Some((_, _, _, bone)) = right {
420 c.right_hand_bone_id = Some(bone);
421 }
422 if let Some((_, raw_driver, hand_driver, _)) = left {
423 c.left_hand_raw_target_id = Some(raw_driver);
424 c.left_hand_visual_target_id = Some(hand_driver);
425 }
426 if let Some((_, raw_driver, hand_driver, _)) = right {
427 c.right_hand_raw_target_id = Some(raw_driver);
428 c.right_hand_visual_target_id = Some(hand_driver);
429 }
430 }
431
432 if !skip_body_pipeline {
443 let body_pipeline_id = world.add_component(TransformForkTRSComponent::new());
444 let body_pipeline_serialize_id = world.add_component(SerializeComponent::off());
445 let map_rot_id = world.add_component(TransformMapRotationComponent::new());
446 let yaw_follow_id = world.add_component(
447 QuatYawFollowComponent::new(body_yaw_threshold, body_yaw_rate)
448 .with_initial_yaw(resolved_initial_body_yaw)
449 .with_forward_plus_z_if(resolved_body_forward_plus_z),
450 );
451
452 let _ = world.set_parent(body_pipeline_serialize_id, Some(body_pipeline_id));
453 let _ = world.set_parent(map_rot_id, Some(body_pipeline_id));
454 let _ = world.set_parent(yaw_follow_id, Some(map_rot_id));
455
456 if let Some(c) = world.get_component_by_id_as_mut::<AvatarControlComponent>(id) {
457 c.body_pipeline_id = Some(body_pipeline_id);
458 }
459
460 emit_attach(emit, id, body_pipeline_id);
461 emit_attach(emit, body_pipeline_id, model_root_id);
462 }
463
464 let mut ik_eye_offset_head_local = eye_offset_head_local;
467 if let Some(y) = head_ik_eye_height {
468 ik_eye_offset_head_local[1] = y;
469 }
470 let neg_eye = [
471 -ik_eye_offset_head_local[0],
472 -ik_eye_offset_head_local[1],
473 -ik_eye_offset_head_local[2],
474 ];
475 let head_target_offset = quat_rotate_vec3(quat_rotation_y(head_ik_offset_yaw), neg_eye);
477
478 let head_target_id = world.add_component(
480 TransformComponent::new()
481 .with_position(
482 head_target_offset[0],
483 head_target_offset[1],
484 head_target_offset[2],
485 )
486 .with_rotation_quat(quat_rotation_y(head_ik_offset_yaw)),
487 );
488 let _ = world.set_parent(head_target_id, Some(driven_t_id));
489
490 emit_attach(emit, head_parent_id, head_splice_id);
491 emit_attach(emit, driven_t_id, head_target_id);
492 emit_attach(emit, head_target_id, head_bone_id);
493
494 emit.push_intent_now(
500 head_bone_id,
501 IntentValue::UpdateTransform {
502 component_ids: vec![head_bone_id],
503 translation: [0.0, 0.0, 0.0],
504 rotation_quat_xyzw: head_rest_rot,
505 scale: head_rest_s,
506 },
507 );
508
509 for (hand_opt, upper_name, lower_name, pole_dir, side_label, grip_rotation_offset) in [
526 (
527 left,
528 left_upper_arm_bone.as_deref(),
529 left_lower_arm_bone.as_deref(),
530 left_arm_pole_direction,
531 "left",
532 hand_grip_rotation_left,
533 ),
534 (
535 right,
536 right_upper_arm_bone.as_deref(),
537 right_lower_arm_bone.as_deref(),
538 right_arm_pole_direction,
539 "right",
540 hand_grip_rotation_right,
541 ),
542 ] {
543 let Some((_, raw_driver, hand_driver, hand_bone)) = hand_opt else {
544 continue;
545 };
546
547 let upper_arm = match upper_name {
548 Some(name) => {
549 let sel = format!("#{}", name);
550 let res = world.find_component(model_root_id, &sel);
551 if res.is_none() {
552 println!(
553 "[AVC] explicit {}_upper_arm_bone \"{}\" not found under model_root — {} arm IK disabled",
554 side_label, name, side_label
555 );
556 }
557 res
558 }
559 None => world
560 .parent_of(hand_bone)
561 .and_then(|lower| world.parent_of(lower)),
562 };
563 let Some(upper_arm) = upper_arm else { continue };
564
565 let lower_arm = match lower_name {
566 Some(name) => {
567 let sel = format!("#{}", name);
568 let res = world.find_component(model_root_id, &sel);
569 if res.is_none() {
570 println!(
571 "[AVC] explicit {}_lower_arm_bone \"{}\" not found under model_root — {} arm IK disabled",
572 side_label, name, side_label
573 );
574 }
575 res
576 }
577 None => world.parent_of(hand_bone),
578 };
579 let Some(lower_arm) = lower_arm else { continue };
580
581 let bone_name =
582 |id: ComponentId| -> String { world.component_name(id).unwrap_or("?").to_string() };
583 let upper_name_s = bone_name(upper_arm);
584 let lower_name_s = bone_name(lower_arm);
585 let hand_name_s = bone_name(hand_bone);
586 println!(
587 "[AVC] {} arm IK: root={} (id={:?}), mid={} (id={:?}), hand={} (id={:?}), target=(id={:?})",
588 side_label,
589 upper_name_s,
590 upper_arm,
591 lower_name_s,
592 lower_arm,
593 hand_name_s,
594 hand_bone,
595 hand_driver,
596 );
597 let looks_suspicious = |n: &str| {
598 n.contains("Twist")
599 || n.contains("Roll")
600 || n.contains("Helper")
601 || n.contains("_collider")
602 || n.contains("J_Sec_")
603 };
604 if looks_suspicious(&upper_name_s) || looks_suspicious(&lower_name_s) {
605 println!(
606 "[AVC] WARNING: {} arm IK resolved to a helper/cloth/collider bone — \
607 set explicit {}_upper_arm_bone(\"...\") and {}_lower_arm_bone(\"...\") \
608 in your AvatarControl block.",
609 side_label, side_label, side_label
610 );
611 }
612
613 let mut chain = IKChainComponent::new(
614 IKSolver::TwoBoneIK {
615 root_joint_id: upper_arm,
616 mid_joint_id: lower_arm,
617 pole_direction: pole_dir,
618 copy_end_rotation: true,
619 },
620 hand_driver,
621 hand_bone,
622 );
623 chain.xr_pose_driver = find_xr_pose_driver(world, hand_driver);
624 let chain_id = world.add_component(chain);
625 let chain_serialize_id = world.add_component(SerializeComponent::off());
626 let _ = world.set_parent(chain_serialize_id, Some(chain_id));
627 if let Some(offset_q) = grip_rotation_offset {
628 println!(
629 "[AVC] {} hand IK target rotation offset = {:?}",
630 side_label, offset_q
631 );
632 } else {
633 let _ = raw_driver;
634 }
635 emit_attach(emit, id, chain_id);
637 }
638
639 if let Some(cam_bone_id) = camera_bone_id {
644 for &(cam, _eye_offset, is_desktop_camera_path) in &camera_children {
645 if is_desktop_camera_path {
646 if let Some(tc) = world.get_component_by_id_as_mut::<TransformComponent>(cam) {
647 if tc.transform.rotation != quat_rotation_y(std::f32::consts::PI) {
648 tc.transform.rotation = quat_rotation_y(std::f32::consts::PI);
649 tc.transform.recompute_model();
650 }
651 } else {
652 let desktop_camera_mount = world.add_component(
653 TransformComponent::new()
654 .with_rotation_quat(quat_rotation_y(std::f32::consts::PI)),
655 );
656 let desktop_camera_mount_serialize_id =
657 world.add_component(SerializeComponent::off());
658 let _ = world.set_parent(
659 desktop_camera_mount_serialize_id,
660 Some(desktop_camera_mount),
661 );
662 emit_attach(emit, desktop_camera_mount, cam);
663 println!(
664 "[AVC] inserted desktop camera yaw-correction mount {:?} for camera {:?}",
665 desktop_camera_mount, cam
666 );
667 emit_attach(emit, cam_bone_id, desktop_camera_mount);
668 continue;
669 }
670 }
671 println!(
672 "[AVC] re-parenting camera {:?} under camera anchor {:?}",
673 cam, cam_bone_id
674 );
675 emit_attach(emit, cam_bone_id, cam);
676 }
677 } else if !camera_children.is_empty() {
678 println!(
679 "[AVC] WARNING: camera children found but camera_bone not resolved — no re-parenting"
680 );
681 }
682}
683
684fn find_xr_pose_driver(world: &World, start: ComponentId) -> Option<ComponentId> {
685 let mut current = Some(start);
686 while let Some(component) = current {
687 if world
688 .get_component_by_id_as::<ControllerXRComponent>(component)
689 .is_some()
690 || world
691 .get_component_by_id_as::<crate::engine::ecs::component::InputXRComponent>(
692 component,
693 )
694 .is_some()
695 {
696 return Some(component);
697 }
698 current = world.parent_of(component);
699 }
700 None
701}
702
703fn resolve_hand_splice(
708 world: &mut World,
709 model_root: ComponentId,
710 bone_name: Option<&str>,
711 controller: Option<ComponentId>,
712 rotation_offset: Option<[f32; 4]>,
713) -> Option<(ComponentId, ComponentId, ComponentId, ComponentId)> {
714 let bone_name = bone_name?;
715 let sel = format!("#{}", bone_name);
716 let bone = world.find_component(model_root, &sel)?;
717 let bone_parent = world.parent_of(bone)?;
718
719 let driver = if let Some(ctrl) = controller {
720 world
721 .children_of(ctrl)
722 .iter()
723 .copied()
724 .find(|&ch| {
725 world
726 .get_component_by_id_as::<TransformComponent>(ch)
727 .is_some()
728 })
729 .unwrap_or_else(|| world.add_component(TransformComponent::new()))
730 } else {
731 world.add_component(TransformComponent::new())
732 };
733
734 let hand_driver = if let Some(offset_q) = rotation_offset {
735 let offset = world.add_component(TransformComponent::new().with_rotation_quat(offset_q));
736 let offset_serialize_id = world.add_component(SerializeComponent::off());
737 let _ = world.set_parent(offset_serialize_id, Some(offset));
738 let _ = world.set_parent(offset, Some(driver));
739 offset
740 } else {
741 driver
742 };
743
744 Some((bone_parent, driver, hand_driver, bone))
745}
746
747fn emit_attach(emit: &mut dyn SignalEmitter, parent: ComponentId, child: ComponentId) {
748 emit.push_intent_now(
749 parent,
750 IntentValue::Attach {
751 parents: vec![parent],
752 child,
753 },
754 );
755}
756
757fn capture_hand_grip_calibration(
758 avc_id: ComponentId,
759 world: &mut World,
760 emit: &mut dyn SignalEmitter,
761) -> bool {
762 let (enabled, left_raw, right_raw, left_visual, right_visual, left_hand, right_hand) = {
763 let Some(c) = world.get_component_by_id_as::<AvatarControlComponent>(avc_id) else {
764 return false;
765 };
766 (
767 c.calibrate_hand_transforms,
768 c.left_hand_raw_target_id,
769 c.right_hand_raw_target_id,
770 c.left_hand_visual_target_id,
771 c.right_hand_visual_target_id,
772 c.left_hand_bone_id,
773 c.right_hand_bone_id,
774 )
775 };
776
777 if !enabled {
778 return false;
779 }
780
781 let (
782 Some(left_raw),
783 Some(right_raw),
784 Some(left_visual),
785 Some(right_visual),
786 Some(left_hand),
787 Some(right_hand),
788 ) = (
789 left_raw,
790 right_raw,
791 left_visual,
792 right_visual,
793 left_hand,
794 right_hand,
795 )
796 else {
797 println!(
798 "[AVC][calibrate] AVC {:?} is enabled for hand calibration but arm targets are not initialized yet.",
799 avc_id
800 );
801 return true;
802 };
803
804 let left_offset = quat_mul(
805 quat_conjugate(tc_world_rot(world, left_raw)),
806 tc_world_rot(world, left_hand),
807 );
808 let right_offset = quat_mul(
809 quat_conjugate(tc_world_rot(world, right_raw)),
810 tc_world_rot(world, right_hand),
811 );
812
813 if let Some(c) = world.get_component_by_id_as_mut::<AvatarControlComponent>(avc_id) {
814 c.hand_grip_rotation_left = Some(left_offset);
815 c.hand_grip_rotation_right = Some(right_offset);
816 }
817
818 if left_visual != left_raw {
819 update_local_rotation(world, emit, left_visual, left_offset);
820 }
821 if right_visual != right_raw {
822 update_local_rotation(world, emit, right_visual, right_offset);
823 }
824
825 println!(
826 "[AVC][calibrate] captured hand grip offsets for AVC {:?}:",
827 avc_id
828 );
829 println!(
830 " hand_grip_rotation_left([{:.7}, {:.7}, {:.7}, {:.7}])",
831 left_offset[0], left_offset[1], left_offset[2], left_offset[3]
832 );
833 println!(
834 " hand_grip_rotation_right([{:.7}, {:.7}, {:.7}, {:.7}])",
835 right_offset[0], right_offset[1], right_offset[2], right_offset[3]
836 );
837
838 true
839}
840
841fn update_local_rotation(
842 world: &World,
843 emit: &mut dyn SignalEmitter,
844 component_id: ComponentId,
845 rotation: [f32; 4],
846) {
847 let (translation, scale) = world
848 .get_component_by_id_as::<TransformComponent>(component_id)
849 .map(|t| (t.transform.translation, t.transform.scale))
850 .unwrap_or(([0.0; 3], [1.0, 1.0, 1.0]));
851 emit.push_intent_now(
852 component_id,
853 IntentValue::UpdateTransform {
854 component_ids: vec![component_id],
855 translation,
856 rotation_quat_xyzw: rotation,
857 scale,
858 },
859 );
860}
861
862fn read_bone_rest_pose(world: &World, bone_id: ComponentId) -> ([f32; 3], [f32; 4], [f32; 3]) {
867 if let Some(rest) = world
868 .children_of(bone_id)
869 .iter()
870 .find_map(|&c| world.get_component_by_id_as::<BoneRestPoseComponent>(c))
871 {
872 return (rest.translation, rest.rotation, rest.scale);
873 }
874 world
875 .get_component_by_id_as::<TransformComponent>(bone_id)
876 .map(|t| {
877 (
878 t.transform.translation,
879 t.transform.rotation,
880 t.transform.scale,
881 )
882 })
883 .unwrap_or(([0.0; 3], [0.0, 0.0, 0.0, 1.0], [1.0, 1.0, 1.0]))
884}
885
886fn tc_world_rot(world: &World, id: ComponentId) -> [f32; 4] {
887 world
888 .get_component_by_id_as::<TransformComponent>(id)
889 .map(|t| mat_to_quat(t.transform.matrix_world))
890 .unwrap_or([0.0, 0.0, 0.0, 1.0])
891}