script/dom/webxr/
xrreferencespace.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 euclid::{Point2D, RigidTransform3D};
7use webxr_api::{self, Floor, Frame, Space};
8
9use crate::dom::bindings::codegen::Bindings::XRReferenceSpaceBinding::{
10    XRReferenceSpaceMethods, XRReferenceSpaceType,
11};
12use crate::dom::bindings::inheritance::Castable;
13use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
14use crate::dom::bindings::root::{Dom, DomRoot};
15use crate::dom::globalscope::GlobalScope;
16use crate::dom::window::Window;
17use crate::dom::xrrigidtransform::XRRigidTransform;
18use crate::dom::xrsession::{ApiPose, BaseTransform, XRSession, cast_transform};
19use crate::dom::xrspace::XRSpace;
20use crate::script_runtime::CanGc;
21
22#[dom_struct]
23pub(crate) struct XRReferenceSpace {
24    xrspace: XRSpace,
25    offset: Dom<XRRigidTransform>,
26    ty: XRReferenceSpaceType,
27}
28
29impl XRReferenceSpace {
30    pub(crate) fn new_inherited(
31        session: &XRSession,
32        offset: &XRRigidTransform,
33        ty: XRReferenceSpaceType,
34    ) -> XRReferenceSpace {
35        XRReferenceSpace {
36            xrspace: XRSpace::new_inherited(session),
37            offset: Dom::from_ref(offset),
38            ty,
39        }
40    }
41
42    pub(crate) fn new(
43        window: &Window,
44        session: &XRSession,
45        ty: XRReferenceSpaceType,
46        can_gc: CanGc,
47    ) -> DomRoot<XRReferenceSpace> {
48        let offset = XRRigidTransform::identity(window, can_gc);
49        Self::new_offset(&window.global(), session, ty, &offset, can_gc)
50    }
51
52    pub(crate) fn new_offset(
53        global: &GlobalScope,
54        session: &XRSession,
55        ty: XRReferenceSpaceType,
56        offset: &XRRigidTransform,
57        can_gc: CanGc,
58    ) -> DomRoot<XRReferenceSpace> {
59        reflect_dom_object(
60            Box::new(XRReferenceSpace::new_inherited(session, offset, ty)),
61            global,
62            can_gc,
63        )
64    }
65
66    pub(crate) fn space(&self) -> Space {
67        let base = match self.ty {
68            XRReferenceSpaceType::Local => webxr_api::BaseSpace::Local,
69            XRReferenceSpaceType::Viewer => webxr_api::BaseSpace::Viewer,
70            XRReferenceSpaceType::Local_floor => webxr_api::BaseSpace::Floor,
71            XRReferenceSpaceType::Bounded_floor => webxr_api::BaseSpace::BoundedFloor,
72            _ => panic!("unsupported reference space found"),
73        };
74        let offset = self.offset.transform();
75        Space { base, offset }
76    }
77
78    pub(crate) fn ty(&self) -> XRReferenceSpaceType {
79        self.ty
80    }
81}
82
83impl XRReferenceSpaceMethods<crate::DomTypeHolder> for XRReferenceSpace {
84    /// <https://immersive-web.github.io/webxr/#dom-xrreferencespace-getoffsetreferencespace>
85    fn GetOffsetReferenceSpace(&self, new: &XRRigidTransform, can_gc: CanGc) -> DomRoot<Self> {
86        let offset = new.transform().then(&self.offset.transform());
87        let offset = XRRigidTransform::new(self.global().as_window(), offset, can_gc);
88        Self::new_offset(
89            &self.global(),
90            self.upcast::<XRSpace>().session(),
91            self.ty,
92            &offset,
93            CanGc::note(),
94        )
95    }
96
97    // https://www.w3.org/TR/webxr/#dom-xrreferencespace-onreset
98    event_handler!(reset, GetOnreset, SetOnreset);
99}
100
101impl XRReferenceSpace {
102    /// Get a transform that can be used to locate the base space
103    ///
104    /// This is equivalent to `get_pose(self).inverse()` (in column vector notation),
105    /// but with better types
106    pub(crate) fn get_base_transform(&self, base_pose: &Frame) -> Option<BaseTransform> {
107        let pose = self.get_pose(base_pose)?;
108        Some(pose.inverse().cast_unit())
109    }
110
111    /// Gets pose represented by this space
112    ///
113    /// The reference origin used is common between all
114    /// get_pose calls for spaces from the same device, so this can be used to compare
115    /// with other spaces
116    pub(crate) fn get_pose(&self, base_pose: &Frame) -> Option<ApiPose> {
117        let pose = self.get_unoffset_pose(base_pose)?;
118        let offset = self.offset.transform();
119        // pose is a transform from the unoffset space to native space,
120        // offset is a transform from offset space to unoffset space,
121        // we want a transform from unoffset space to native space,
122        // which is pose * offset in column vector notation
123        Some(offset.then(&pose))
124    }
125
126    /// Gets pose represented by this space
127    ///
128    /// Does not apply originOffset, use get_viewer_pose instead if you need it
129    pub(crate) fn get_unoffset_pose(&self, base_pose: &Frame) -> Option<ApiPose> {
130        match self.ty {
131            XRReferenceSpaceType::Local => {
132                // The eye-level pose is basically whatever the headset pose was at t=0, which
133                // for most devices is (0, 0, 0)
134                Some(RigidTransform3D::identity())
135            },
136            XRReferenceSpaceType::Local_floor | XRReferenceSpaceType::Bounded_floor => {
137                let native_to_floor = self
138                    .upcast::<XRSpace>()
139                    .session()
140                    .with_session(|s| s.floor_transform())?;
141                Some(cast_transform(native_to_floor.inverse()))
142            },
143            XRReferenceSpaceType::Viewer => {
144                Some(cast_transform(base_pose.pose.as_ref()?.transform))
145            },
146            _ => unimplemented!(),
147        }
148    }
149
150    pub(crate) fn get_bounds(&self) -> Option<Vec<Point2D<f32, Floor>>> {
151        self.upcast::<XRSpace>()
152            .session()
153            .with_session(|s| s.reference_space_bounds())
154    }
155}