script/dom/webxr/
xrray.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::{Angle, RigidTransform3D, Rotation3D, Vector3D};
7use js::rust::HandleObject;
8use js::typedarray::{Float32, HeapFloat32Array};
9use script_bindings::trace::RootedTraceableBox;
10use webxr_api::{ApiSpace, Ray};
11
12use crate::dom::bindings::buffer_source::HeapBufferSource;
13use crate::dom::bindings::codegen::Bindings::DOMPointBinding::DOMPointInit;
14use crate::dom::bindings::codegen::Bindings::XRRayBinding::{XRRayDirectionInit, XRRayMethods};
15use crate::dom::bindings::error::{Error, Fallible};
16use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
17use crate::dom::bindings::root::DomRoot;
18use crate::dom::dompointreadonly::DOMPointReadOnly;
19use crate::dom::window::Window;
20use crate::dom::xrrigidtransform::XRRigidTransform;
21use crate::script_runtime::{CanGc, JSContext};
22
23#[dom_struct]
24pub(crate) struct XRRay {
25    reflector_: Reflector,
26    #[ignore_malloc_size_of = "defined in webxr"]
27    #[no_trace]
28    ray: Ray<ApiSpace>,
29    #[ignore_malloc_size_of = "defined in mozjs"]
30    matrix: HeapBufferSource<Float32>,
31}
32
33impl XRRay {
34    fn new_inherited(ray: Ray<ApiSpace>) -> XRRay {
35        XRRay {
36            reflector_: Reflector::new(),
37            ray,
38            matrix: HeapBufferSource::default(),
39        }
40    }
41
42    fn new(
43        window: &Window,
44        proto: Option<HandleObject>,
45        ray: Ray<ApiSpace>,
46        can_gc: CanGc,
47    ) -> DomRoot<XRRay> {
48        reflect_dom_object_with_proto(Box::new(XRRay::new_inherited(ray)), window, proto, can_gc)
49    }
50
51    pub(crate) fn ray(&self) -> Ray<ApiSpace> {
52        self.ray
53    }
54}
55
56impl XRRayMethods<crate::DomTypeHolder> for XRRay {
57    /// <https://immersive-web.github.io/hit-test/#dom-xrray-xrray>
58    fn Constructor(
59        window: &Window,
60        proto: Option<HandleObject>,
61        can_gc: CanGc,
62        origin: &DOMPointInit,
63        direction: &XRRayDirectionInit,
64    ) -> Fallible<DomRoot<Self>> {
65        if origin.w != 1.0 {
66            return Err(Error::Type("Origin w coordinate must be 1".into()));
67        }
68        if *direction.w != 0.0 {
69            return Err(Error::Type("Direction w coordinate must be 0".into()));
70        }
71        if *direction.x == 0.0 && *direction.y == 0.0 && *direction.z == 0.0 {
72            return Err(Error::Type(
73                "Direction vector cannot have zero length".into(),
74            ));
75        }
76
77        let origin = Vector3D::new(origin.x as f32, origin.y as f32, origin.z as f32);
78        let direction = Vector3D::new(
79            *direction.x as f32,
80            *direction.y as f32,
81            *direction.z as f32,
82        )
83        .normalize();
84
85        Ok(Self::new(window, proto, Ray { origin, direction }, can_gc))
86    }
87
88    /// <https://immersive-web.github.io/hit-test/#dom-xrray-xrray-transform>
89    fn Constructor_(
90        window: &Window,
91        proto: Option<HandleObject>,
92        can_gc: CanGc,
93        transform: &XRRigidTransform,
94    ) -> Fallible<DomRoot<Self>> {
95        let transform = transform.transform();
96        let origin = transform.translation;
97        let direction = transform
98            .rotation
99            .transform_vector3d(Vector3D::new(0., 0., -1.));
100
101        Ok(Self::new(window, proto, Ray { origin, direction }, can_gc))
102    }
103
104    /// <https://immersive-web.github.io/hit-test/#dom-xrray-origin>
105    fn Origin(&self, can_gc: CanGc) -> DomRoot<DOMPointReadOnly> {
106        DOMPointReadOnly::new(
107            &self.global(),
108            self.ray.origin.x as f64,
109            self.ray.origin.y as f64,
110            self.ray.origin.z as f64,
111            1.,
112            can_gc,
113        )
114    }
115
116    /// <https://immersive-web.github.io/hit-test/#dom-xrray-direction>
117    fn Direction(&self, can_gc: CanGc) -> DomRoot<DOMPointReadOnly> {
118        DOMPointReadOnly::new(
119            &self.global(),
120            self.ray.direction.x as f64,
121            self.ray.direction.y as f64,
122            self.ray.direction.z as f64,
123            0.,
124            can_gc,
125        )
126    }
127
128    /// <https://immersive-web.github.io/hit-test/#dom-xrray-matrix>
129    fn Matrix(&self, _cx: JSContext, can_gc: CanGc) -> RootedTraceableBox<HeapFloat32Array> {
130        // https://immersive-web.github.io/hit-test/#xrray-obtain-the-matrix
131        if !self.matrix.is_initialized() {
132            // Step 1
133            let z = Vector3D::new(0., 0., -1.);
134            // Step 2
135            let axis = z.cross(self.ray.direction);
136            // Step 3
137            let cos_angle = z.dot(self.ray.direction);
138            // Step 4
139            let rotation = if cos_angle > -1. && cos_angle < 1. {
140                Rotation3D::around_axis(axis, Angle::radians(cos_angle.acos()))
141            } else if cos_angle == -1. {
142                let axis = Vector3D::new(1., 0., 0.);
143                Rotation3D::around_axis(axis, Angle::radians(cos_angle.acos()))
144            } else {
145                Rotation3D::identity()
146            };
147            // Step 5
148            let translation = self.ray.origin;
149            // Step 6
150            // According to the spec all matrices are column-major,
151            // however euclid uses row vectors so we use .to_array()
152            let arr = RigidTransform3D::new(rotation, translation)
153                .to_transform()
154                .to_array();
155            self.matrix
156                .set_data(_cx, &arr, can_gc)
157                .expect("Failed to set matrix data on XRRAy.")
158        }
159
160        self.matrix
161            .get_typed_array()
162            .expect("Failed to get matrix from XRRay.")
163    }
164}