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