webxr: Update hand input to match latest spec (#32958)

* Update IDLs

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Update XRHand and XRJointSpace methods/bindings

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Implement fillJointRadii

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Implement fillPoses

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Formatting

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Update test expectations

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Tidy, missing spec link

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Remove idlharness expectation files, update hands pref

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Update interfaces

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* XRJointPose interface

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* XRHand interface

Signed-off-by: Daniel Adams <msub2official@gmail.com>

---------

Signed-off-by: Daniel Adams <msub2official@gmail.com>
This commit is contained in:
Daniel Adams 2024-08-14 12:45:45 -10:00 committed by GitHub
parent 057873c94a
commit 825d6f10e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 314 additions and 553 deletions

View file

@ -10,6 +10,16 @@ interface XRFrame {
[Throws] XRViewerPose? getViewerPose(XRReferenceSpace referenceSpace);
[Throws] XRPose? getPose(XRSpace space, XRSpace relativeTo);
[Pref="dom.webxr.hands.enabled", Throws] XRJointPose? getJointPose(XRJointSpace space, XRSpace relativeTo);
// WebXR Hand Input
[Pref="dom.webxr.hands.enabled", Throws]
XRJointPose? getJointPose(XRJointSpace joint, XRSpace baseSpace);
[Pref="dom.webxr.hands.enabled", Throws]
boolean fillJointRadii(sequence<XRJointSpace> jointSpaces, Float32Array radii);
[Pref="dom.webxr.hands.enabled", Throws]
boolean fillPoses(sequence<XRSpace> spaces, XRSpace baseSpace, Float32Array transforms);
// WebXR Hit Test
sequence<XRHitTestResult> getHitTestResults(XRHitTestSource hitTestSource);
};

View file

@ -4,38 +4,43 @@
// https://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md
enum XRHandJoint {
"wrist",
"thumb-metacarpal",
"thumb-phalanx-proximal",
"thumb-phalanx-distal",
"thumb-tip",
"index-finger-metacarpal",
"index-finger-phalanx-proximal",
"index-finger-phalanx-intermediate",
"index-finger-phalanx-distal",
"index-finger-tip",
"middle-finger-metacarpal",
"middle-finger-phalanx-proximal",
"middle-finger-phalanx-intermediate",
"middle-finger-phalanx-distal",
"middle-finger-tip",
"ring-finger-metacarpal",
"ring-finger-phalanx-proximal",
"ring-finger-phalanx-intermediate",
"ring-finger-phalanx-distal",
"ring-finger-tip",
"pinky-finger-metacarpal",
"pinky-finger-phalanx-proximal",
"pinky-finger-phalanx-intermediate",
"pinky-finger-phalanx-distal",
"pinky-finger-tip"
};
[SecureContext, Exposed=Window, Pref="dom.webxr.hands.enabled"]
interface XRHand {
readonly attribute long length;
getter XRJointSpace(unsigned long index);
iterable<XRHandJoint, XRJointSpace>;
const unsigned long WRIST = 0;
const unsigned long THUMB_METACARPAL = 1;
const unsigned long THUMB_PHALANX_PROXIMAL = 2;
const unsigned long THUMB_PHALANX_DISTAL = 3;
const unsigned long THUMB_PHALANX_TIP = 4;
const unsigned long INDEX_METACARPAL = 5;
const unsigned long INDEX_PHALANX_PROXIMAL = 6;
const unsigned long INDEX_PHALANX_INTERMEDIATE = 7;
const unsigned long INDEX_PHALANX_DISTAL = 8;
const unsigned long INDEX_PHALANX_TIP = 9;
const unsigned long MIDDLE_METACARPAL = 10;
const unsigned long MIDDLE_PHALANX_PROXIMAL = 11;
const unsigned long MIDDLE_PHALANX_INTERMEDIATE = 12;
const unsigned long MIDDLE_PHALANX_DISTAL = 13;
const unsigned long MIDDLE_PHALANX_TIP = 14;
const unsigned long RING_METACARPAL = 15;
const unsigned long RING_PHALANX_PROXIMAL = 16;
const unsigned long RING_PHALANX_INTERMEDIATE = 17;
const unsigned long RING_PHALANX_DISTAL = 18;
const unsigned long RING_PHALANX_TIP = 19;
const unsigned long LITTLE_METACARPAL = 20;
const unsigned long LITTLE_PHALANX_PROXIMAL = 21;
const unsigned long LITTLE_PHALANX_INTERMEDIATE = 22;
const unsigned long LITTLE_PHALANX_DISTAL = 23;
const unsigned long LITTLE_PHALANX_TIP = 24;
readonly attribute unsigned long size;
XRJointSpace get(XRHandJoint key);
};

View file

@ -5,4 +5,6 @@
// https://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md
[SecureContext, Exposed=Window, Pref="dom.webxr.hands.enabled"]
interface XRJointSpace: XRSpace {};
interface XRJointSpace: XRSpace {
readonly attribute XRHandJoint jointName;
};

View file

@ -5,6 +5,8 @@
use std::cell::Cell;
use dom_struct::dom_struct;
use js::gc::CustomAutoRooterGuard;
use js::typedarray::Float32Array;
use webxr_api::{Frame, LayerId, SubImages};
use crate::dom::bindings::codegen::Bindings::XRFrameBinding::XRFrameMethods;
@ -175,4 +177,107 @@ impl XRFrameMethods for XRFrame {
.map(|r| XRHitTestResult::new(&self.global(), *r, self))
.collect()
}
#[allow(unsafe_code)]
/// <https://www.w3.org/TR/webxr-hand-input-1/#dom-xrframe-filljointradii>
fn FillJointRadii(
&self,
joint_spaces: Vec<DomRoot<XRJointSpace>>,
mut radii: CustomAutoRooterGuard<Float32Array>,
) -> Result<bool, Error> {
if !self.active.get() {
return Err(Error::InvalidState);
}
for joint_space in &joint_spaces {
if self.session != joint_space.upcast::<XRSpace>().session() {
return Err(Error::InvalidState);
}
}
if joint_spaces.len() > radii.len() {
return Err(Error::Type(
"Length of radii does not match length of joint spaces".to_string(),
));
}
let mut radii_vec = radii.to_vec();
let mut all_valid = true;
radii_vec.iter_mut().enumerate().for_each(|(i, radius)| {
if let Some(joint_frame) = joint_spaces
.get(i)
.and_then(|joint_space| joint_space.frame(&self.data))
{
*radius = joint_frame.radius;
} else {
all_valid = false;
}
});
if !all_valid {
radii_vec.fill(f32::NAN);
}
unsafe {
radii.update(&radii_vec);
}
Ok(all_valid)
}
#[allow(unsafe_code)]
/// <https://www.w3.org/TR/webxr-hand-input-1/#dom-xrframe-fillposes>
fn FillPoses(
&self,
spaces: Vec<DomRoot<XRSpace>>,
base_space: &XRSpace,
mut transforms: CustomAutoRooterGuard<Float32Array>,
) -> Result<bool, Error> {
if !self.active.get() {
return Err(Error::InvalidState);
}
for space in &spaces {
if self.session != space.session() {
return Err(Error::InvalidState);
}
}
if self.session != base_space.session() {
return Err(Error::InvalidState);
}
if spaces.len() * 16 > transforms.len() {
return Err(Error::Type(
"Transforms array length does not match 16 * spaces length".to_string(),
));
}
let mut transforms_vec = transforms.to_vec();
let mut all_valid = true;
spaces.iter().enumerate().for_each(|(i, space)| {
let Some(joint_pose) = self.get_pose(space) else {
all_valid = false;
return;
};
let Some(base_pose) = self.get_pose(base_space) else {
all_valid = false;
return;
};
let pose = joint_pose.then(&base_pose.inverse());
let elements = pose.to_transform();
let elements_arr = elements.to_array();
transforms_vec[i * 16..(i + 1) * 16].copy_from_slice(&elements_arr);
});
if !all_valid {
transforms_vec.fill(f32::NAN);
}
unsafe {
transforms.update(&transforms_vec);
}
Ok(all_valid)
}
}

View file

@ -5,13 +5,101 @@
use dom_struct::dom_struct;
use webxr_api::{FingerJoint, Hand, Joint};
use crate::dom::bindings::codegen::Bindings::XRHandBinding::{XRHandConstants, XRHandMethods};
use crate::dom::bindings::codegen::Bindings::XRHandBinding::{XRHandJoint, XRHandMethods};
use crate::dom::bindings::iterable::Iterable;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::globalscope::GlobalScope;
use crate::dom::xrinputsource::XRInputSource;
use crate::dom::xrjointspace::XRJointSpace;
const JOINT_SPACE_MAP: [(XRHandJoint, Joint); 25] = [
(XRHandJoint::Wrist, Joint::Wrist),
(XRHandJoint::Thumb_metacarpal, Joint::ThumbMetacarpal),
(
XRHandJoint::Thumb_phalanx_proximal,
Joint::ThumbPhalanxProximal,
),
(XRHandJoint::Thumb_phalanx_distal, Joint::ThumbPhalanxDistal),
(XRHandJoint::Thumb_tip, Joint::ThumbPhalanxTip),
(
XRHandJoint::Index_finger_metacarpal,
Joint::Index(FingerJoint::Metacarpal),
),
(
XRHandJoint::Index_finger_phalanx_proximal,
Joint::Index(FingerJoint::PhalanxProximal),
),
(XRHandJoint::Index_finger_phalanx_intermediate, {
Joint::Index(FingerJoint::PhalanxIntermediate)
}),
(
XRHandJoint::Index_finger_phalanx_distal,
Joint::Index(FingerJoint::PhalanxDistal),
),
(
XRHandJoint::Index_finger_tip,
Joint::Index(FingerJoint::PhalanxTip),
),
(
XRHandJoint::Middle_finger_metacarpal,
Joint::Middle(FingerJoint::Metacarpal),
),
(
XRHandJoint::Middle_finger_phalanx_proximal,
Joint::Middle(FingerJoint::PhalanxProximal),
),
(XRHandJoint::Middle_finger_phalanx_intermediate, {
Joint::Middle(FingerJoint::PhalanxIntermediate)
}),
(
XRHandJoint::Middle_finger_phalanx_distal,
Joint::Middle(FingerJoint::PhalanxDistal),
),
(
XRHandJoint::Middle_finger_tip,
Joint::Middle(FingerJoint::PhalanxTip),
),
(
XRHandJoint::Ring_finger_metacarpal,
Joint::Ring(FingerJoint::Metacarpal),
),
(
XRHandJoint::Ring_finger_phalanx_proximal,
Joint::Ring(FingerJoint::PhalanxProximal),
),
(XRHandJoint::Ring_finger_phalanx_intermediate, {
Joint::Ring(FingerJoint::PhalanxIntermediate)
}),
(
XRHandJoint::Ring_finger_phalanx_distal,
Joint::Ring(FingerJoint::PhalanxDistal),
),
(
XRHandJoint::Ring_finger_tip,
Joint::Ring(FingerJoint::PhalanxTip),
),
(
XRHandJoint::Pinky_finger_metacarpal,
Joint::Little(FingerJoint::Metacarpal),
),
(
XRHandJoint::Pinky_finger_phalanx_proximal,
Joint::Little(FingerJoint::PhalanxProximal),
),
(XRHandJoint::Pinky_finger_phalanx_intermediate, {
Joint::Little(FingerJoint::PhalanxIntermediate)
}),
(
XRHandJoint::Pinky_finger_phalanx_distal,
Joint::Little(FingerJoint::PhalanxDistal),
),
(
XRHandJoint::Pinky_finger_tip,
Joint::Little(FingerJoint::PhalanxTip),
),
];
#[dom_struct]
pub struct XRHand {
reflector_: Reflector,
@ -34,57 +122,55 @@ impl XRHand {
pub fn new(global: &GlobalScope, source: &XRInputSource, support: Hand<()>) -> DomRoot<XRHand> {
let id = source.id();
let session = source.session();
let spaces = support
.map(|field, joint| field.map(|_| XRJointSpace::new(global, session, id, joint)));
let spaces = support.map(|field, joint| {
let hand_joint = JOINT_SPACE_MAP
.iter()
.find(|&&(_, value)| value == joint)
.map(|&(hand_joint, _)| hand_joint)
.expect("Invalid joint name");
field.map(|_| XRJointSpace::new(global, session, id, joint, hand_joint))
});
reflect_dom_object(Box::new(XRHand::new_inherited(source, &spaces)), global)
}
}
impl XRHandMethods for XRHand {
/// <https://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md>
fn Length(&self) -> i32 {
XRHandConstants::LITTLE_PHALANX_TIP as i32 + 1
fn Size(&self) -> u32 {
XRHandJoint::Pinky_finger_tip as u32 + 1
}
/// <https://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md>
fn IndexedGetter(&self, joint_index: u32) -> Option<DomRoot<XRJointSpace>> {
let joint = match joint_index {
XRHandConstants::WRIST => Joint::Wrist,
XRHandConstants::THUMB_METACARPAL => Joint::ThumbMetacarpal,
XRHandConstants::THUMB_PHALANX_PROXIMAL => Joint::ThumbPhalanxProximal,
XRHandConstants::THUMB_PHALANX_DISTAL => Joint::ThumbPhalanxDistal,
XRHandConstants::THUMB_PHALANX_TIP => Joint::ThumbPhalanxTip,
XRHandConstants::INDEX_METACARPAL => Joint::Index(FingerJoint::Metacarpal),
XRHandConstants::INDEX_PHALANX_PROXIMAL => Joint::Index(FingerJoint::PhalanxProximal),
XRHandConstants::INDEX_PHALANX_INTERMEDIATE => {
Joint::Index(FingerJoint::PhalanxIntermediate)
},
XRHandConstants::INDEX_PHALANX_DISTAL => Joint::Index(FingerJoint::PhalanxDistal),
XRHandConstants::INDEX_PHALANX_TIP => Joint::Index(FingerJoint::PhalanxTip),
XRHandConstants::MIDDLE_METACARPAL => Joint::Middle(FingerJoint::Metacarpal),
XRHandConstants::MIDDLE_PHALANX_PROXIMAL => Joint::Middle(FingerJoint::PhalanxProximal),
XRHandConstants::MIDDLE_PHALANX_INTERMEDIATE => {
Joint::Middle(FingerJoint::PhalanxIntermediate)
},
XRHandConstants::MIDDLE_PHALANX_DISTAL => Joint::Middle(FingerJoint::PhalanxDistal),
XRHandConstants::MIDDLE_PHALANX_TIP => Joint::Middle(FingerJoint::PhalanxTip),
XRHandConstants::RING_METACARPAL => Joint::Ring(FingerJoint::Metacarpal),
XRHandConstants::RING_PHALANX_PROXIMAL => Joint::Ring(FingerJoint::PhalanxProximal),
XRHandConstants::RING_PHALANX_INTERMEDIATE => {
Joint::Ring(FingerJoint::PhalanxIntermediate)
},
XRHandConstants::RING_PHALANX_DISTAL => Joint::Ring(FingerJoint::PhalanxDistal),
XRHandConstants::RING_PHALANX_TIP => Joint::Ring(FingerJoint::PhalanxTip),
XRHandConstants::LITTLE_METACARPAL => Joint::Little(FingerJoint::Metacarpal),
XRHandConstants::LITTLE_PHALANX_PROXIMAL => Joint::Little(FingerJoint::PhalanxProximal),
XRHandConstants::LITTLE_PHALANX_INTERMEDIATE => {
Joint::Little(FingerJoint::PhalanxIntermediate)
},
XRHandConstants::LITTLE_PHALANX_DISTAL => Joint::Little(FingerJoint::PhalanxDistal),
XRHandConstants::LITTLE_PHALANX_TIP => Joint::Little(FingerJoint::PhalanxTip),
// XXXManishearth should this be a TypeError?
_ => return None,
};
self.spaces.get(joint).map(|j| DomRoot::from_ref(&**j))
fn Get(&self, joint_name: XRHandJoint) -> DomRoot<XRJointSpace> {
let joint = JOINT_SPACE_MAP
.iter()
.find(|&&(key, _)| key == joint_name)
.map(|&(_, joint)| joint)
.expect("Invalid joint name");
self.spaces
.get(joint)
.map(|j| DomRoot::from_ref(&**j))
.expect("Failed to get joint pose")
}
}
impl Iterable for XRHand {
type Key = XRHandJoint;
type Value = DomRoot<XRJointSpace>;
fn get_iterable_length(&self) -> u32 {
JOINT_SPACE_MAP.len() as u32
}
fn get_value_at_index(&self, n: u32) -> DomRoot<XRJointSpace> {
let joint = JOINT_SPACE_MAP[n as usize].1;
self.spaces
.get(joint)
.map(|j| DomRoot::from_ref(&**j))
.expect("Failed to get joint pose")
}
fn get_key_at_index(&self, n: u32) -> XRHandJoint {
JOINT_SPACE_MAP[n as usize].0
}
}

View file

@ -6,6 +6,8 @@ use dom_struct::dom_struct;
use euclid::RigidTransform3D;
use webxr_api::{BaseSpace, Frame, InputId, Joint, JointFrame, Space};
use crate::dom::bindings::codegen::Bindings::XRHandBinding::XRHandJoint;
use crate::dom::bindings::codegen::Bindings::XRJointSpaceBinding::XRJointSpaceMethods;
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::DomRoot;
use crate::dom::globalscope::GlobalScope;
@ -21,14 +23,21 @@ pub struct XRJointSpace {
#[ignore_malloc_size_of = "defined in rust-webxr"]
#[no_trace]
joint: Joint,
hand_joint: XRHandJoint,
}
impl XRJointSpace {
pub fn new_inherited(session: &XRSession, input: InputId, joint: Joint) -> XRJointSpace {
pub fn new_inherited(
session: &XRSession,
input: InputId,
joint: Joint,
hand_joint: XRHandJoint,
) -> XRJointSpace {
XRJointSpace {
xrspace: XRSpace::new_inherited(session),
input,
joint,
hand_joint,
}
}
@ -38,8 +47,12 @@ impl XRJointSpace {
session: &XRSession,
input: InputId,
joint: Joint,
hand_joint: XRHandJoint,
) -> DomRoot<XRJointSpace> {
reflect_dom_object(Box::new(Self::new_inherited(session, input, joint)), global)
reflect_dom_object(
Box::new(Self::new_inherited(session, input, joint, hand_joint)),
global,
)
}
pub fn space(&self) -> Space {
@ -61,3 +74,10 @@ impl XRJointSpace {
self.frame(frame).map(|f| f.pose).map(|t| t.cast_unit())
}
}
impl XRJointSpaceMethods for XRJointSpace {
/// <https://www.w3.org/TR/webxr-hand-input-1/#xrjointspace-jointname>
fn JointName(&self) -> XRHandJoint {
self.hand_joint
}
}