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