Skip to main content

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