Skip to main content

mittens_engine/engine/ecs/component/
controller_xr.rs

1use crate::engine::ecs::ComponentId;
2use crate::engine::ecs::component::Component;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub enum ControllerHand {
6    Left,
7    Right,
8}
9
10impl Default for ControllerHand {
11    fn default() -> Self {
12        Self::Left
13    }
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum ControllerPoseKind {
18    /// A pointing pose, typically used for ray-based UI interaction.
19    Aim,
20    /// A “held object” pose, typically used for attaching models/tools.
21    Grip,
22}
23
24impl Default for ControllerPoseKind {
25    fn default() -> Self {
26        Self::Aim
27    }
28}
29
30/// Marker/config for an XR controller tracked pose.
31///
32/// Semantics:
33/// - Attach a `TransformComponent` as a child of this component.
34/// - the active XR runtime will drive that transform child from controller pose tracking.
35#[derive(Debug, Clone, Default)]
36pub struct ControllerXRComponent {
37    pub enabled: bool,
38    /// Runtime-only: true after a valid controller pose was applied this frame.
39    pub pose_valid: bool,
40    pub hand: ControllerHand,
41    pub pose: ControllerPoseKind,
42
43    // Cached ECS id (runtime-only). Filled during init.
44    pub component_id: Option<ComponentId>,
45}
46
47impl ControllerXRComponent {
48    pub fn new(enabled: bool, hand: ControllerHand, pose: ControllerPoseKind) -> Self {
49        Self {
50            enabled,
51            pose_valid: false,
52            hand,
53            pose,
54            component_id: None,
55        }
56    }
57
58    pub fn on_left_aim() -> Self {
59        Self::new(true, ControllerHand::Left, ControllerPoseKind::Aim)
60    }
61
62    pub fn on_right_aim() -> Self {
63        Self::new(true, ControllerHand::Right, ControllerPoseKind::Aim)
64    }
65}
66
67impl Component for ControllerXRComponent {
68    fn as_any(&self) -> &dyn std::any::Any {
69        self
70    }
71
72    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
73        self
74    }
75
76    fn name(&self) -> &'static str {
77        "xr_hand"
78    }
79
80    fn set_id(&mut self, component: ComponentId) {
81        self.component_id = Some(component);
82    }
83
84    fn init(&mut self, emit: &mut dyn crate::engine::ecs::SignalEmitter, component: ComponentId) {
85        self.component_id = Some(component);
86        emit.push_intent_now(
87            component,
88            crate::engine::ecs::IntentValue::RegisterControllerXr {
89                component_ids: vec![component],
90            },
91        );
92    }
93
94    fn cleanup(
95        &mut self,
96        emit: &mut dyn crate::engine::ecs::SignalEmitter,
97        component: ComponentId,
98    ) {
99        emit.push_intent_now(
100            component,
101            crate::engine::ecs::IntentValue::RemoveControllerXr {
102                component_ids: vec![component],
103            },
104        );
105    }
106
107    fn to_mms_ast(
108        &self,
109        _world: &crate::engine::ecs::World,
110    ) -> crate::scripting::ast::ComponentExpression {
111        use crate::engine::ecs::component::ce_helpers::*;
112        let hand = match self.hand {
113            ControllerHand::Left => "Left",
114            ControllerHand::Right => "Right",
115        };
116        let pose = match self.pose {
117            ControllerPoseKind::Aim => "Aim",
118            ControllerPoseKind::Grip => "Grip",
119        };
120        ce_call("XRHand", "new", vec![b(self.enabled), s(hand), s(pose)])
121    }
122}