script/dom/webxr/
xrhand.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use dom_struct::dom_struct;
6use js::jsapi::JSContext;
7use js::rust::MutableHandleValue;
8use webxr_api::{FingerJoint, Hand, Joint};
9
10use crate::dom::bindings::codegen::Bindings::XRHandBinding::{XRHandJoint, XRHandMethods};
11use crate::dom::bindings::conversions::ToJSValConvertible;
12use crate::dom::bindings::iterable::Iterable;
13use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
14use crate::dom::bindings::root::{Dom, DomRoot};
15use crate::dom::globalscope::GlobalScope;
16use crate::dom::xrinputsource::XRInputSource;
17use crate::dom::xrjointspace::XRJointSpace;
18use crate::script_runtime::CanGc;
19
20const JOINT_SPACE_MAP: [(XRHandJoint, Joint); 25] = [
21    (XRHandJoint::Wrist, Joint::Wrist),
22    (XRHandJoint::Thumb_metacarpal, Joint::ThumbMetacarpal),
23    (
24        XRHandJoint::Thumb_phalanx_proximal,
25        Joint::ThumbPhalanxProximal,
26    ),
27    (XRHandJoint::Thumb_phalanx_distal, Joint::ThumbPhalanxDistal),
28    (XRHandJoint::Thumb_tip, Joint::ThumbPhalanxTip),
29    (
30        XRHandJoint::Index_finger_metacarpal,
31        Joint::Index(FingerJoint::Metacarpal),
32    ),
33    (
34        XRHandJoint::Index_finger_phalanx_proximal,
35        Joint::Index(FingerJoint::PhalanxProximal),
36    ),
37    (XRHandJoint::Index_finger_phalanx_intermediate, {
38        Joint::Index(FingerJoint::PhalanxIntermediate)
39    }),
40    (
41        XRHandJoint::Index_finger_phalanx_distal,
42        Joint::Index(FingerJoint::PhalanxDistal),
43    ),
44    (
45        XRHandJoint::Index_finger_tip,
46        Joint::Index(FingerJoint::PhalanxTip),
47    ),
48    (
49        XRHandJoint::Middle_finger_metacarpal,
50        Joint::Middle(FingerJoint::Metacarpal),
51    ),
52    (
53        XRHandJoint::Middle_finger_phalanx_proximal,
54        Joint::Middle(FingerJoint::PhalanxProximal),
55    ),
56    (XRHandJoint::Middle_finger_phalanx_intermediate, {
57        Joint::Middle(FingerJoint::PhalanxIntermediate)
58    }),
59    (
60        XRHandJoint::Middle_finger_phalanx_distal,
61        Joint::Middle(FingerJoint::PhalanxDistal),
62    ),
63    (
64        XRHandJoint::Middle_finger_tip,
65        Joint::Middle(FingerJoint::PhalanxTip),
66    ),
67    (
68        XRHandJoint::Ring_finger_metacarpal,
69        Joint::Ring(FingerJoint::Metacarpal),
70    ),
71    (
72        XRHandJoint::Ring_finger_phalanx_proximal,
73        Joint::Ring(FingerJoint::PhalanxProximal),
74    ),
75    (XRHandJoint::Ring_finger_phalanx_intermediate, {
76        Joint::Ring(FingerJoint::PhalanxIntermediate)
77    }),
78    (
79        XRHandJoint::Ring_finger_phalanx_distal,
80        Joint::Ring(FingerJoint::PhalanxDistal),
81    ),
82    (
83        XRHandJoint::Ring_finger_tip,
84        Joint::Ring(FingerJoint::PhalanxTip),
85    ),
86    (
87        XRHandJoint::Pinky_finger_metacarpal,
88        Joint::Little(FingerJoint::Metacarpal),
89    ),
90    (
91        XRHandJoint::Pinky_finger_phalanx_proximal,
92        Joint::Little(FingerJoint::PhalanxProximal),
93    ),
94    (XRHandJoint::Pinky_finger_phalanx_intermediate, {
95        Joint::Little(FingerJoint::PhalanxIntermediate)
96    }),
97    (
98        XRHandJoint::Pinky_finger_phalanx_distal,
99        Joint::Little(FingerJoint::PhalanxDistal),
100    ),
101    (
102        XRHandJoint::Pinky_finger_tip,
103        Joint::Little(FingerJoint::PhalanxTip),
104    ),
105];
106
107#[dom_struct]
108pub(crate) struct XRHand {
109    reflector_: Reflector,
110    #[ignore_malloc_size_of = "defined in webxr"]
111    source: Dom<XRInputSource>,
112    #[ignore_malloc_size_of = "partially defind in webxr"]
113    #[custom_trace]
114    spaces: Hand<Dom<XRJointSpace>>,
115}
116
117impl XRHand {
118    fn new_inherited(source: &XRInputSource, spaces: &Hand<DomRoot<XRJointSpace>>) -> XRHand {
119        XRHand {
120            reflector_: Reflector::new(),
121            source: Dom::from_ref(source),
122            spaces: spaces.map(|j, _| j.as_ref().map(|j| Dom::from_ref(&**j))),
123        }
124    }
125
126    pub(crate) fn new(
127        global: &GlobalScope,
128        source: &XRInputSource,
129        support: Hand<()>,
130        can_gc: CanGc,
131    ) -> DomRoot<XRHand> {
132        let id = source.id();
133        let session = source.session();
134        let spaces = support.map(|field, joint| {
135            let hand_joint = JOINT_SPACE_MAP
136                .iter()
137                .find(|&&(_, value)| value == joint)
138                .map(|&(hand_joint, _)| hand_joint)
139                .expect("Invalid joint name");
140            field.map(|_| XRJointSpace::new(global, session, id, joint, hand_joint, CanGc::note()))
141        });
142        reflect_dom_object(
143            Box::new(XRHand::new_inherited(source, &spaces)),
144            global,
145            can_gc,
146        )
147    }
148}
149
150impl XRHandMethods<crate::DomTypeHolder> for XRHand {
151    /// <https://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md>
152    fn Size(&self) -> u32 {
153        XRHandJoint::Pinky_finger_tip as u32 + 1
154    }
155
156    /// <https://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md>
157    fn Get(&self, joint_name: XRHandJoint) -> DomRoot<XRJointSpace> {
158        let joint = JOINT_SPACE_MAP
159            .iter()
160            .find(|&&(key, _)| key == joint_name)
161            .map(|&(_, joint)| joint)
162            .expect("Invalid joint name");
163        self.spaces
164            .get(joint)
165            .map(|j| DomRoot::from_ref(&**j))
166            .expect("Failed to get joint pose")
167    }
168}
169
170/// A wrapper to work around a crown error—Root<T> has a crown annotation on it that is not present
171/// on the Iterable::Value associated type. The absence is harmless in this case.
172pub(crate) struct ValueWrapper(pub DomRoot<XRJointSpace>);
173
174impl ToJSValConvertible for ValueWrapper {
175    #[allow(unsafe_code)]
176    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
177        self.0.to_jsval(cx, rval)
178    }
179}
180
181impl Iterable for XRHand {
182    type Key = XRHandJoint;
183    type Value = ValueWrapper;
184
185    fn get_iterable_length(&self) -> u32 {
186        JOINT_SPACE_MAP.len() as u32
187    }
188
189    fn get_value_at_index(&self, n: u32) -> ValueWrapper {
190        let joint = JOINT_SPACE_MAP[n as usize].1;
191        self.spaces
192            .get(joint)
193            .map(|j| ValueWrapper(DomRoot::from_ref(&**j)))
194            .expect("Failed to get joint pose")
195    }
196
197    fn get_key_at_index(&self, n: u32) -> XRHandJoint {
198        JOINT_SPACE_MAP[n as usize].0
199    }
200}