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