script/dom/webxr/
xrview.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 std::cell::Cell;
6
7use dom_struct::dom_struct;
8use euclid::RigidTransform3D;
9use js::typedarray::{Float32, HeapFloat32Array};
10use script_bindings::trace::RootedTraceableBox;
11use webxr_api::{ApiSpace, View};
12
13use crate::dom::bindings::buffer_source::HeapBufferSource;
14use crate::dom::bindings::codegen::Bindings::XRViewBinding::{XREye, XRViewMethods};
15use crate::dom::bindings::num::Finite;
16use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
17use crate::dom::bindings::root::{Dom, DomRoot};
18use crate::dom::globalscope::GlobalScope;
19use crate::dom::window::Window;
20use crate::dom::xrrigidtransform::XRRigidTransform;
21use crate::dom::xrsession::{BaseSpace, BaseTransform, XRSession, cast_transform};
22use crate::script_runtime::{CanGc, JSContext};
23
24#[dom_struct]
25pub(crate) struct XRView {
26    reflector_: Reflector,
27    session: Dom<XRSession>,
28    eye: XREye,
29    viewport_index: usize,
30    #[ignore_malloc_size_of = "mozjs"]
31    proj: HeapBufferSource<Float32>,
32    #[ignore_malloc_size_of = "defined in rust-webxr"]
33    #[no_trace]
34    view: View<ApiSpace>,
35    transform: Dom<XRRigidTransform>,
36    requested_viewport_scale: Cell<f64>,
37}
38
39impl XRView {
40    fn new_inherited(
41        session: &XRSession,
42        transform: &XRRigidTransform,
43        eye: XREye,
44        viewport_index: usize,
45        view: View<ApiSpace>,
46    ) -> XRView {
47        XRView {
48            reflector_: Reflector::new(),
49            session: Dom::from_ref(session),
50            eye,
51            viewport_index,
52            proj: HeapBufferSource::default(),
53            view,
54            transform: Dom::from_ref(transform),
55            requested_viewport_scale: Cell::new(1.0),
56        }
57    }
58
59    pub(crate) fn new<V: Copy>(
60        window: &Window,
61        session: &XRSession,
62        view: &View<V>,
63        eye: XREye,
64        viewport_index: usize,
65        to_base: &BaseTransform,
66        can_gc: CanGc,
67    ) -> DomRoot<XRView> {
68        let transform: RigidTransform3D<f32, V, BaseSpace> = view.transform.then(to_base);
69        let transform = XRRigidTransform::new(window, cast_transform(transform), can_gc);
70
71        reflect_dom_object(
72            Box::new(XRView::new_inherited(
73                session,
74                &transform,
75                eye,
76                viewport_index,
77                view.cast_unit(),
78            )),
79            window,
80            can_gc,
81        )
82    }
83
84    pub(crate) fn session(&self) -> &XRSession {
85        &self.session
86    }
87
88    pub(crate) fn viewport_index(&self) -> usize {
89        self.viewport_index
90    }
91}
92
93impl XRViewMethods<crate::DomTypeHolder> for XRView {
94    /// <https://immersive-web.github.io/webxr/#dom-xrview-eye>
95    fn Eye(&self) -> XREye {
96        self.eye
97    }
98
99    /// <https://immersive-web.github.io/webxr/#dom-xrview-projectionmatrix>
100    fn ProjectionMatrix(
101        &self,
102        _cx: JSContext,
103        can_gc: CanGc,
104    ) -> RootedTraceableBox<HeapFloat32Array> {
105        if !self.proj.is_initialized() {
106            let cx = GlobalScope::get_cx();
107            // row_major since euclid uses row vectors
108            let proj = self.view.projection.to_array();
109            self.proj
110                .set_data(cx, &proj, can_gc)
111                .expect("Failed to set projection matrix.")
112        }
113        self.proj
114            .get_typed_array()
115            .expect("Failed to get projection matrix.")
116    }
117
118    /// <https://immersive-web.github.io/webxr/#dom-xrview-transform>
119    fn Transform(&self) -> DomRoot<XRRigidTransform> {
120        DomRoot::from_ref(&self.transform)
121    }
122
123    /// <https://www.w3.org/TR/webxr/#dom-xrview-recommendedviewportscale>
124    fn GetRecommendedViewportScale(&self) -> Option<Finite<f64>> {
125        // Just return 1.0 since we currently will always use full-sized viewports
126        Finite::new(1.0)
127    }
128
129    /// <https://www.w3.org/TR/webxr/#dom-xrview-requestviewportscale>
130    fn RequestViewportScale(&self, scale: Option<Finite<f64>>) {
131        if let Some(scale) = scale {
132            if *scale > 0.0 {
133                let clamped_scale = scale.clamp(0.0, 1.0);
134                self.requested_viewport_scale.set(clamped_scale);
135            }
136        }
137    }
138
139    /// <https://www.w3.org/TR/webxr-ar-module-1/#dom-xrview-isfirstpersonobserver>
140    fn IsFirstPersonObserver(&self) -> bool {
141        // Servo is not currently supported anywhere that supports this, so return false
142        false
143    }
144}