1use crate::{
16 fields::FieldAspect,
17 node::NodeResult,
18 spatial::{SpatialRefAspect, Transform},
19};
20use glam::{FloatExt, Quat, Vec3A, vec3a};
21use stardust_xr::values::*;
22use std::hash::Hash;
23
24pub use crate::protocol::input::*;
25
26impl InputMethod {
27 pub fn create(
28 spatial_parent: &impl SpatialRefAspect,
29 transform: Transform,
30 input_type: InputDataType,
31 datamap: &Datamap,
32 ) -> NodeResult<Self> {
33 let client = spatial_parent.client();
34 create_input_method(
35 client,
36 client.generate_id(),
37 spatial_parent,
38 transform,
39 input_type,
40 datamap,
41 )
42 }
43}
44impl InputHandler {
45 pub fn create(
46 spatial_parent: &impl SpatialRefAspect,
47 transform: Transform,
48 field: &impl FieldAspect,
49 ) -> NodeResult<Self> {
50 let client = spatial_parent.client();
51 create_input_handler(
52 client,
53 client.generate_id(),
54 spatial_parent,
55 transform,
56 field,
57 )
58 }
59}
60impl Default for Joint {
61 fn default() -> Self {
62 Joint {
63 position: [0.0; 3].into(),
64 rotation: Quat::IDENTITY.into(),
65 radius: 0.0,
66 distance: 0.0,
67 }
68 }
69}
70impl Default for Finger {
71 fn default() -> Self {
72 Finger {
73 tip: Default::default(),
74 distal: Default::default(),
75 intermediate: Default::default(),
76 proximal: Default::default(),
77 metacarpal: Default::default(),
78 }
79 }
80}
81impl Default for Thumb {
82 fn default() -> Self {
83 Thumb {
84 tip: Default::default(),
85 distal: Default::default(),
86 proximal: Default::default(),
87 metacarpal: Default::default(),
88 }
89 }
90}
91impl Default for Hand {
92 fn default() -> Self {
93 Hand {
94 right: Default::default(),
95 thumb: Default::default(),
96 index: Default::default(),
97 middle: Default::default(),
98 ring: Default::default(),
99 little: Default::default(),
100 palm: Default::default(),
101 wrist: Default::default(),
102 elbow: Default::default(),
103 }
104 }
105}
106impl Default for Pointer {
107 fn default() -> Self {
108 Pointer {
109 origin: [0.0; 3].into(),
110 orientation: Quat::IDENTITY.into(),
111 deepest_point: [0.0; 3].into(),
112 }
113 }
114}
115impl Default for Tip {
116 fn default() -> Self {
117 Tip {
118 origin: [0.0; 3].into(),
119 orientation: Quat::IDENTITY.into(),
120 }
121 }
122}
123
124impl Finger {
127 pub fn length(&self) -> f32 {
129 let proximal_position: Vec3A = self.proximal.position.into();
130 let intermediate_position: Vec3A = self.intermediate.position.into();
131 let distal_position: Vec3A = self.distal.position.into();
132 let tip_position: Vec3A = self.tip.position.into();
133
134 proximal_position.distance(intermediate_position)
135 + intermediate_position.distance(distal_position)
136 + distal_position.distance(tip_position)
137 }
138
139 pub fn direction(&self) -> Vector3<f32> {
140 let proximal_position: Vec3A = self.proximal.position.into();
141 let tip_position: Vec3A = self.tip.position.into();
142
143 (tip_position - proximal_position).normalize().into()
144 }
145}
146
147impl Thumb {
148 pub fn length(&self) -> f32 {
150 let proximal_position: Vec3A = self.proximal.position.into();
151 let distal_position: Vec3A = self.distal.position.into();
152 let tip_position: Vec3A = self.tip.position.into();
153
154 proximal_position.distance(distal_position) + distal_position.distance(tip_position)
155 }
156
157 pub fn direction(&self) -> Vector3<f32> {
158 let proximal_position: Vec3A = self.proximal.position.into();
159 let tip_position: Vec3A = self.tip.position.into();
160
161 (tip_position - proximal_position).normalize().into()
162 }
163}
164
165impl Hand {
166 pub fn palm_normal(&self) -> Vector3<f32> {
168 (Quat::from(self.palm.rotation) * vec3a(0.0, -1.0, 0.0)).into()
169 }
170 pub fn radial_axis(&self) -> Vector3<f32> {
172 (Quat::from(self.palm.rotation)
173 * if self.right {
174 vec3a(-1.0, 0.0, 0.0)
175 } else {
176 vec3a(1.0, 0.0, 0.0)
177 })
178 .into()
179 }
180 pub fn distal_axis(&self) -> Vector3<f32> {
182 (Quat::from(self.palm.rotation) * vec3a(0.0, 0.0, -1.0)).into()
183 }
184
185 pub fn finger_curl(&self, finger: &Finger) -> f32 {
186 let distal_axis: Vec3A = self.distal_axis().into();
187 let direction: Vec3A = finger.direction().into();
188 direction.dot(-distal_axis).remap(-1.0, 1.0, 0.0, 1.0)
189 }
190
191 pub fn thumb_curl(&self) -> f32 {
192 let radial_axis: Vec3A = self.radial_axis().into();
193 let thumb_direction: Vec3A = self.thumb.direction().into();
194 thumb_direction.dot(-radial_axis).remap(-1.0, 1.0, 0.0, 1.0)
195 }
196
197 pub fn pinch_distance(&self, finger: &Finger) -> f32 {
198 let thumb_tip: Vec3A = self.thumb.tip.position.into();
199 let index_tip: Vec3A = finger.tip.position.into();
200 thumb_tip.distance(index_tip)
201 }
202
203 pub fn pinch_position(&self) -> Vector3<f32> {
205 let thumb_tip: Vec3A = self.thumb.tip.position.into();
206 let index_tip: Vec3A = self.index.tip.position.into();
207
208 ((2.0 * thumb_tip + index_tip) * 0.3333333).into()
209 }
210
211 pub fn stable_pinch_position(&self) -> Vector3<f32> {
215 let index_knuckle: Vec3A = self.index.proximal.position.into();
216
217 let index_length = self.index.length();
218
219 let radial_axis: Vec3A = self.radial_axis().into();
220 let palm_normal: Vec3A = self.palm_normal().into();
221 let distal_axis: Vec3A = self.distal_axis().into();
222
223 let stable_pinch_position = index_knuckle
224 + (palm_normal * index_length * 0.85)
225 + (distal_axis * index_length * 0.20)
226 + (radial_axis * index_length * 0.20);
227
228 stable_pinch_position.into()
229 }
230
231 pub fn predicted_pinch_position(&self) -> Vector3<f32> {
233 let thumb_tip: Vec3A = self.thumb.tip.position.into();
234 let index_tip: Vec3A = self.index.tip.position.into();
235
236 let index_knuckle: Vec3A = self.index.proximal.position.into();
237 let index_length = self.index.length();
238
239 let radial_axis: Vec3A = self.radial_axis().into();
240 let palm_normal: Vec3A = self.palm_normal().into();
241 let distal_axis: Vec3A = self.distal_axis().into();
242
243 let thumb_influence = (thumb_tip - index_knuckle)
244 .normalize()
245 .dot(radial_axis)
246 .remap(0.0, 1.0, 0.5, 0.0);
247
248 let mut predicted_pinch_point = index_knuckle
249 + palm_normal * index_length * 0.85
250 + distal_axis * index_length * 0.20
251 + radial_axis * index_length * 0.20;
252
253 predicted_pinch_point = predicted_pinch_point.lerp(thumb_tip, thumb_influence);
254 predicted_pinch_point = predicted_pinch_point.lerp(index_tip, 0.15);
255
256 predicted_pinch_point.into()
257 }
258
259 fn hand_scale(&self) -> f32 {
260 let index_metacarpal: Vec3A = self.index.metacarpal.position.into();
261 let index_proximal: Vec3A = self.index.proximal.position.into();
262
263 let middle_metacarpal: Vec3A = self.middle.metacarpal.position.into();
264 let middle_proximal: Vec3A = self.middle.proximal.position.into();
265
266 let ring_metacarpal: Vec3A = self.ring.metacarpal.position.into();
267 let ring_proximal: Vec3A = self.ring.proximal.position.into();
268
269 let little_metacarpal: Vec3A = self.little.metacarpal.position.into();
270 let little_proximal: Vec3A = self.little.proximal.position.into();
271
272 let index_metacarpal_length = index_metacarpal.distance(index_proximal);
273 let middle_metacarpal_length = middle_metacarpal.distance(middle_proximal);
274 let ring_metacarpal_length = ring_metacarpal.distance(ring_proximal);
275 let little_metacarpal_length = little_metacarpal.distance(little_proximal);
276
277 let mut scale = 0.0;
278
279 scale += index_metacarpal_length / 0.06812;
280 scale += middle_metacarpal_length / 0.06460;
281 scale += ring_metacarpal_length / 0.05800;
282 scale += little_metacarpal_length / 0.05369;
283
284 scale / 4.0
285 }
286
287 pub fn pinch_strength(&self) -> f32 {
289 let thumb_tip: Vec3A = self.thumb.tip.position.into();
290 let index_tip: Vec3A = self.index.tip.position.into();
291 let middle_tip: Vec3A = self.middle.tip.position.into();
292 let ring_tip: Vec3A = self.ring.tip.position.into();
293 let little_tip: Vec3A = self.little.tip.position.into();
294
295 let min_distance = index_tip
296 .distance_squared(thumb_tip)
297 .min(middle_tip.distance_squared(thumb_tip))
298 .min(ring_tip.distance_squared(thumb_tip))
299 .min(little_tip.distance_squared(thumb_tip))
300 .sqrt();
301
302 let scale = self.hand_scale();
303 let distance_zero = 0.0600 * scale;
304 let distance_one = 0.0220 * scale;
305
306 ((min_distance - distance_zero) / (distance_one - distance_zero)).clamp(0.0, 1.0)
307 }
308
309 pub fn fist_strength(&self) -> f32 {
311 let radial_axis: Vec3A = self.radial_axis().into();
312 let distal_axis: Vec3A = self.distal_axis().into();
313
314 let thumb_direction: Vec3A = self.thumb.direction().into();
315 let index_direction: Vec3A = self.index.direction().into();
316 let middle_direction: Vec3A = self.middle.direction().into();
317 let ring_direction: Vec3A = self.ring.direction().into();
318 let little_direction: Vec3A = self.little.direction().into();
319
320 (thumb_direction.dot(-radial_axis)
321 + index_direction.dot(-distal_axis)
322 + middle_direction.dot(-distal_axis)
323 + ring_direction.dot(-distal_axis)
324 + little_direction.dot(-distal_axis))
325 .remap(-5.0, 5.0, 0.0, 1.0)
326 }
327}
328
329impl Pointer {
330 pub fn direction(&self) -> Vector3<f32> {
331 (Quat::from(self.orientation) * vec3a(0.0, 0.0, -1.0)).into()
332 }
333}
334
335impl Hash for InputData {
336 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
337 self.id.hash(state)
338 }
339}
340impl PartialEq for InputData {
341 fn eq(&self, other: &Self) -> bool {
342 self.id.eq(&other.id)
343 }
344}
345impl Eq for InputData {}
346
347#[tokio::test]
348async fn fusion_input_handler() {
349 use crate::Client;
350
351 let mut client = Client::connect().await.expect("Couldn't connect");
352
353 let field = super::fields::Field::create(
354 client.get_root(),
355 Transform::identity(),
356 crate::fields::Shape::Sphere(0.1),
357 )
358 .unwrap();
359 let _input_handler =
360 InputHandler::create(client.get_root(), Transform::none(), &field).unwrap();
361
362 client
363 .sync_event_loop(|_, _| {
364 while let Some(input_event) = _input_handler.recv_input_handler_event() {
365 match input_event {
366 InputHandlerEvent::Input { methods: _, data } => on_input(data),
367 }
368 }
369 })
370 .await
371 .unwrap();
372
373 fn on_input(data: Vec<InputData>) {
374 for data in data {
375 dbg!(data.id);
376 dbg!(data.distance);
377 match &data.input {
378 InputDataType::Pointer(_) => {
379 println!("Pointer input");
380 }
381 InputDataType::Hand(_) => {
382 println!("Hand input");
383 data.datamap.with_data(|datamap| {
384 dbg!(
385 datamap
386 .iter_keys()
387 .zip(datamap.iter_values())
388 .collect::<Vec<_>>()
389 );
390 let _ = dbg!(datamap.idx("right").get_bool());
391 });
392 }
393 InputDataType::Tip(_) => {
394 println!("Tip input");
395 }
396 }
397 }
398 }
399}
400
401#[tokio::test]
402async fn fusion_pointer_input_method() {
403 use crate::Client;
404 use crate::drawable::Model;
405 use crate::root::*;
406 use crate::spatial::SpatialAspect;
407
408 let mut client = Client::connect().await.expect("Couldn't connect");
409
410 let mut fbb = stardust_xr::schemas::flex::flexbuffers::Builder::default();
411 fbb.start_map();
412 let pointer = InputMethod::create(
413 client.get_root(),
414 Transform::none(),
415 InputDataType::Pointer(Pointer::default()),
416 &Datamap::from_typed(PointerData::default()).unwrap(),
417 )
418 .unwrap();
419 let _model = Model::create(
420 &pointer,
421 Transform::from_rotation_scale(
422 glam::Quat::from_rotation_x(std::f32::consts::PI * 0.5),
423 [0.1; 3],
424 ),
425 &stardust_xr::values::ResourceID::new_namespaced("fusion", "cursor_spike"),
426 )
427 .unwrap();
428 let mut datamap = PointerData::default();
429
430 #[derive(Default, serde::Serialize, serde::Deserialize)]
431 struct PointerData {
432 grab: f32,
433 select: f32,
434 }
435
436 client
437 .sync_event_loop(|client, _| {
438 while let Some(root_event) = client.get_root().recv_root_event() {
439 match root_event {
440 RootEvent::Ping { response } => {
441 response.send_ok(());
442 }
443 RootEvent::Frame { info } => {
444 let (sin, cos) = info.elapsed.sin_cos();
445 pointer
446 .set_local_transform(Transform::from_translation([
447 sin * 0.1,
448 0.0,
449 cos * 0.1,
450 ]))
451 .unwrap();
452
453 datamap.grab = sin;
454 pointer
455 .set_datamap(&Datamap::from_typed(&datamap).unwrap())
456 .unwrap();
457 }
458 RootEvent::SaveState { response } => response.send_ok(Default::default()),
459 }
460 }
461 })
462 .await
463 .unwrap();
464}
465
466#[tokio::test]
467async fn fusion_tip_input_method() {
468 use crate::Client;
469 use crate::drawable::Model;
470 use crate::root::*;
471 use crate::spatial::SpatialAspect;
472
473 let mut client = Client::connect().await.expect("Couldn't connect");
474
475 let tip = InputMethod::create(
476 client.get_root(),
477 Transform::none(),
478 InputDataType::Tip(Tip::default()),
479 &Datamap::from_typed(TipData::default()).unwrap(),
480 )
481 .unwrap();
482
483 fn summon_model(parent: &impl SpatialAspect, rotation: glam::Quat) -> Model {
484 Model::create(
485 parent,
486 Transform::from_rotation_scale(rotation, [0.1; 3]),
487 &stardust_xr::values::ResourceID::new_namespaced("fusion", "cursor_spike"),
488 )
489 .unwrap()
490 }
491
492 struct Cursor {
493 top: Model,
494 bottom: Model,
495 left: Model,
496 right: Model,
497 forward: Model,
498 backward: Model,
499 }
500 #[derive(Default, serde::Serialize, serde::Deserialize)]
501 struct TipData {
502 grab: f32,
503 select: f32,
504 }
505 let mut datamap = TipData::default();
506
507 client
508 .sync_event_loop(|client, _| {
509 while let Some(root_event) = client.get_root().recv_root_event() {
510 match root_event {
511 RootEvent::Ping { response } => {
512 response.send_ok(());
513 }
514 RootEvent::Frame { info } => {
515 let (sin, cos) = info.elapsed.sin_cos();
516 tip.set_local_transform(Transform::from_translation([
517 sin * 0.1,
518 0.0,
519 cos * 0.1,
520 ]))
521 .unwrap();
522
523 datamap.grab = sin;
524 tip.set_datamap(&Datamap::from_typed(&datamap).unwrap())
525 .unwrap();
526 }
527 RootEvent::SaveState { response } => response.send_ok(Default::default()),
528 }
529 }
530 })
531 .await
532 .unwrap();
533}