mittens_engine/engine/ecs/system/
pointer_system.rs1use crate::engine::ecs::component::{
2 ControllerXRComponent, InputComponent, InputXRComponent, PointerComponent, RayCastComponent,
3};
4use crate::engine::ecs::system::XrInputState;
5use crate::engine::ecs::{ComponentId, SignalEmitter, World};
6use crate::engine::user_input::InputState;
7use std::collections::HashMap;
8use winit::event::MouseButton;
9
10#[derive(Debug, Default)]
19pub struct PointerSystem {
20 pointer_to_raycast: HashMap<ComponentId, ComponentId>,
21}
22
23impl PointerSystem {
24 pub fn register_pointer(
25 &mut self,
26 world: &mut World,
27 component: ComponentId,
28 emit: &mut dyn SignalEmitter,
29 ) {
30 let Some(pointer) = world.get_component_by_id_as::<PointerComponent>(component) else {
31 return;
32 };
33
34 if !pointer.enabled {
35 self.pointer_to_raycast.remove(&component);
36 return;
37 }
38
39 let existing_raycast = world.children_of(component).iter().copied().find(|&child| {
40 world
41 .get_component_by_id_as::<RayCastComponent>(child)
42 .is_some()
43 });
44
45 let raycast = match existing_raycast {
46 Some(raycast) => raycast,
47 None => {
48 let raycast = world.add_component(RayCastComponent::event_driven());
49 if world.add_child(component, raycast).is_err() {
50 return;
51 }
52 world.init_component_tree(raycast, emit);
53 raycast
54 }
55 };
56
57 self.pointer_to_raycast.insert(component, raycast);
58 }
59
60 pub fn remove_pointer(&mut self, component: ComponentId) {
61 self.pointer_to_raycast.remove(&component);
62 }
63
64 pub fn raycast_for_pointer(&self, component: ComponentId) -> Option<ComponentId> {
65 self.pointer_to_raycast.get(&component).copied()
66 }
67
68 pub fn raycast_to_pointer(&self, raycaster: ComponentId) -> Option<ComponentId> {
69 self.pointer_to_raycast
70 .iter()
71 .find(|(_, rc)| **rc == raycaster)
72 .map(|(&ptr, _)| ptr)
73 }
74}
75
76#[derive(Debug, Clone, Copy, Default)]
80pub struct PointerTopologyContext {
81 pub has_desktop_input_driver: bool,
82 pub has_xr_input_driver: bool,
83 pub has_controller_driver: bool,
84 pub has_desktop_camera_anchor: bool,
85 pub has_xr_camera_anchor: bool,
86}
87
88pub fn has_ancestor_component<T: 'static>(world: &World, start: ComponentId) -> bool {
90 let mut cur = start;
91 while let Some(parent) = world.parent_of(cur) {
92 if world.get_component_by_id_as::<T>(parent).is_some() {
93 return true;
94 }
95 cur = parent;
96 }
97 false
98}
99
100pub fn nearest_ancestor_transform(world: &World, start: ComponentId) -> Option<ComponentId> {
102 if world
103 .get_component_by_id_as::<crate::engine::ecs::component::TransformComponent>(start)
104 .is_some()
105 {
106 return Some(start);
107 }
108 let mut cur = start;
109 while let Some(parent) = world.parent_of(cur) {
110 if world
111 .get_component_by_id_as::<crate::engine::ecs::component::TransformComponent>(parent)
112 .is_some()
113 {
114 return Some(parent);
115 }
116 cur = parent;
117 }
118 None
119}
120
121pub fn pointer_topology_context(world: &World, cid: ComponentId) -> PointerTopologyContext {
126 PointerTopologyContext {
127 has_desktop_input_driver: has_ancestor_component::<InputComponent>(world, cid),
128 has_xr_input_driver: has_ancestor_component::<InputXRComponent>(world, cid),
129 has_controller_driver: has_ancestor_component::<ControllerXRComponent>(world, cid),
130 has_desktop_camera_anchor: has_ancestor_component::<
131 crate::engine::ecs::component::Camera3DComponent,
132 >(world, cid)
133 || has_ancestor_component::<crate::engine::ecs::component::Camera2DComponent>(
134 world, cid,
135 ),
136 has_xr_camera_anchor: has_ancestor_component::<
137 crate::engine::ecs::component::CameraXRComponent,
138 >(world, cid),
139 }
140}
141
142#[derive(Default, Debug, Clone)]
147pub struct PointerActivations {
148 pub pressed: Vec<ComponentId>,
149 pub down: Vec<ComponentId>,
150 pub released: Vec<ComponentId>,
151}
152
153impl PointerSystem {
154 pub fn build_activations(
161 &self,
162 world: &World,
163 input: &InputState,
164 xr: &XrInputState,
165 ) -> PointerActivations {
166 let mut act = PointerActivations::default();
167
168 for (&pointer_cid, _) in &self.pointer_to_raycast {
169 let topo = pointer_topology_context(world, pointer_cid);
170
171 if topo.has_controller_driver {
172 let hand_idx = controller_hand_index(world, pointer_cid);
174 let Some(i) = hand_idx else { continue };
175 if xr.trigger_pressed[i] {
176 act.pressed.push(pointer_cid);
177 }
178 if xr.trigger_down[i] {
179 act.down.push(pointer_cid);
180 }
181 if xr.trigger_released[i] {
182 act.released.push(pointer_cid);
183 }
184 } else if !topo.has_xr_camera_anchor && !topo.has_xr_input_driver {
185 if input.mouse_pressed.contains(&MouseButton::Left) {
187 act.pressed.push(pointer_cid);
188 }
189 if input.mouse_down.contains(&MouseButton::Left) {
190 act.down.push(pointer_cid);
191 }
192 if input.mouse_released.contains(&MouseButton::Left) {
193 act.released.push(pointer_cid);
194 }
195 }
196 }
197
198 act
199 }
200}
201
202fn controller_hand_index(world: &World, start: ComponentId) -> Option<usize> {
204 let mut cur = start;
205 loop {
206 if let Some(c) = world.get_component_by_id_as::<ControllerXRComponent>(cur) {
207 return Some(match c.hand {
208 crate::engine::ecs::component::ControllerHand::Left => 0,
209 crate::engine::ecs::component::ControllerHand::Right => 1,
210 });
211 }
212 cur = world.parent_of(cur)?;
213 }
214}