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, Float32Array};
10use webxr_api::{ApiSpace, View};
11
12use crate::dom::bindings::buffer_source::HeapBufferSource;
13use crate::dom::bindings::codegen::Bindings::XRViewBinding::{XREye, XRViewMethods};
14use crate::dom::bindings::num::Finite;
15use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
16use crate::dom::bindings::root::{Dom, DomRoot};
17use crate::dom::globalscope::GlobalScope;
18use crate::dom::window::Window;
19use crate::dom::xrrigidtransform::XRRigidTransform;
20use crate::dom::xrsession::{BaseSpace, BaseTransform, XRSession, cast_transform};
21use crate::script_runtime::{CanGc, JSContext};
22
23#[dom_struct]
24pub(crate) struct XRView {
25    reflector_: Reflector,
26    session: Dom<XRSession>,
27    eye: XREye,
28    viewport_index: usize,
29    #[ignore_malloc_size_of = "mozjs"]
30    proj: HeapBufferSource<Float32>,
31    #[ignore_malloc_size_of = "defined in rust-webxr"]
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(&self, _cx: JSContext, can_gc: CanGc) -> Float32Array {
100        if !self.proj.is_initialized() {
101            let cx = GlobalScope::get_cx();
102            // row_major since euclid uses row vectors
103            let proj = self.view.projection.to_array();
104            self.proj
105                .set_data(cx, &proj, can_gc)
106                .expect("Failed to set projection matrix.")
107        }
108        self.proj
109            .get_typed_array()
110            .expect("Failed to get projection matrix.")
111    }
112
113    /// <https://immersive-web.github.io/webxr/#dom-xrview-transform>
114    fn Transform(&self) -> DomRoot<XRRigidTransform> {
115        DomRoot::from_ref(&self.transform)
116    }
117
118    /// <https://www.w3.org/TR/webxr/#dom-xrview-recommendedviewportscale>
119    fn GetRecommendedViewportScale(&self) -> Option<Finite<f64>> {
120        // Just return 1.0 since we currently will always use full-sized viewports
121        Finite::new(1.0)
122    }
123
124    /// <https://www.w3.org/TR/webxr/#dom-xrview-requestviewportscale>
125    fn RequestViewportScale(&self, scale: Option<Finite<f64>>) {
126        if let Some(scale) = scale {
127            if *scale > 0.0 {
128                let clamped_scale = scale.clamp(0.0, 1.0);
129                self.requested_viewport_scale.set(clamped_scale);
130            }
131        }
132    }
133
134    /// <https://www.w3.org/TR/webxr-ar-module-1/#dom-xrview-isfirstpersonobserver>
135    fn IsFirstPersonObserver(&self) -> bool {
136        // Servo is not currently supported anywhere that supports this, so return false
137        false
138    }
139}