Skip to main content

mittens_engine/engine/ecs/component/
avatar_body_yaw.rs

1use crate::engine::ecs::ComponentId;
2use crate::engine::ecs::component::Component;
3
4/// Sits between the avatar body pipeline root and `model_root`.
5/// Tracks head yaw and smoothly rotates the body to follow when the
6/// relative yaw exceeds `threshold`.
7///
8/// Topology:
9/// ```text
10/// TransformForkTRS (body_pipeline)
11///   AvatarBodyYawComponent   ← this node
12///     TransformComponent (model_root, Y-offset + base rotation)
13///       GLTFComponent
14/// ```
15#[derive(Debug, Clone)]
16pub struct AvatarBodyYawComponent {
17    /// Yaw delta (radians) that triggers body rotation. Default: π/4 (45°).
18    pub threshold: f32,
19
20    /// Body rotation rate (radians/sec). Default: 3.0 rad/s.
21    pub rate: f32,
22
23    /// ComponentId of the HMD-driven TransformComponent to read yaw from
24    /// (`avatar_driven_t` in vr-input). Wired at scene construction.
25    pub hmd_driven_transform: Option<ComponentId>,
26
27    /// Current world-space body yaw (radians). Initialized to π to match the
28    /// model_root's initial `rotation_euler(0, π, 0)` base flip.
29    pub(crate) body_yaw: f32,
30
31    /// When true, use +Z as the forward axis when extracting yaw from the driven
32    /// transform's world matrix. Use for desktop/keyboard setups with
33    /// `InputTransformModeComponent::forward_z()`. Default false = -Z (OpenXR).
34    pub forward_plus_z: bool,
35
36    component: Option<ComponentId>,
37}
38
39impl AvatarBodyYawComponent {
40    pub fn new() -> Self {
41        Self::default()
42    }
43
44    pub fn with_threshold(mut self, t: f32) -> Self {
45        self.threshold = t;
46        self
47    }
48
49    pub fn with_rate(mut self, r: f32) -> Self {
50        self.rate = r;
51        self
52    }
53
54    pub fn with_hmd_driven_transform(mut self, id: ComponentId) -> Self {
55        self.hmd_driven_transform = Some(id);
56        self
57    }
58
59    /// Override the starting body yaw (radians). Defaults to π to match the vr-input
60    /// model_root base flip. Set to 0.0 for setups with no base rotation on model_root.
61    pub fn with_initial_yaw(mut self, yaw: f32) -> Self {
62        self.body_yaw = yaw;
63        self
64    }
65
66    /// Use +Z as the forward axis for yaw extraction. Required for desktop setups
67    /// using `InputTransformModeComponent::forward_z()`.
68    pub fn with_forward_plus_z(mut self) -> Self {
69        self.forward_plus_z = true;
70        self
71    }
72}
73
74impl Default for AvatarBodyYawComponent {
75    fn default() -> Self {
76        Self {
77            threshold: std::f32::consts::FRAC_PI_4,
78            rate: 3.0,
79            hmd_driven_transform: None,
80            body_yaw: std::f32::consts::PI,
81            forward_plus_z: false,
82            component: None,
83        }
84    }
85}
86
87impl Component for AvatarBodyYawComponent {
88    fn name(&self) -> &'static str {
89        "avatar_body_yaw"
90    }
91
92    fn set_id(&mut self, id: ComponentId) {
93        self.component = Some(id);
94    }
95
96    fn init(&mut self, emit: &mut dyn crate::engine::ecs::SignalEmitter, component: ComponentId) {
97        emit.push_intent_now(
98            component,
99            crate::engine::ecs::IntentValue::RegisterAvatarBodyYaw {
100                component_ids: vec![component],
101            },
102        );
103    }
104
105    fn as_any(&self) -> &dyn std::any::Any {
106        self
107    }
108
109    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
110        self
111    }
112
113    fn to_mms_ast(
114        &self,
115        _world: &crate::engine::ecs::World,
116    ) -> crate::scripting::ast::ComponentExpression {
117        use crate::engine::ecs::component::ce_helpers::*;
118        let mut c = ce("AvatarBodyYaw")
119            .with_call("threshold", vec![num(self.threshold as f64)])
120            .with_call("rate", vec![num(self.rate as f64)]);
121        if self.forward_plus_z {
122            c = c.with_call("forward_plus_z", vec![]);
123        }
124        c
125    }
126}