webxr_api/
util.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 euclid::Transform3D;
6use serde::{Deserialize, Serialize};
7
8use crate::{FrameUpdateEvent, HitTestId, HitTestSource};
9
10#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
11pub struct ClipPlanes {
12    pub near: f32,
13    pub far: f32,
14    /// Was there an update that needs propagation to the client?
15    update: bool,
16}
17
18impl Default for ClipPlanes {
19    fn default() -> Self {
20        ClipPlanes {
21            near: 0.1,
22            far: 1000.,
23            update: false,
24        }
25    }
26}
27
28impl ClipPlanes {
29    pub fn update(&mut self, near: f32, far: f32) {
30        self.near = near;
31        self.far = far;
32        self.update = true;
33    }
34
35    /// Checks for and clears the pending update flag
36    pub fn recently_updated(&mut self) -> bool {
37        if self.update {
38            self.update = false;
39            true
40        } else {
41            false
42        }
43    }
44}
45
46#[derive(Clone, Debug, Default, Serialize, Deserialize)]
47/// Holds on to hit tests
48pub struct HitTestList {
49    tests: Vec<HitTestSource>,
50    uncommitted_tests: Vec<HitTestSource>,
51}
52
53impl HitTestList {
54    pub fn request_hit_test(&mut self, source: HitTestSource) {
55        self.uncommitted_tests.push(source)
56    }
57
58    pub fn commit_tests(&mut self) -> Vec<FrameUpdateEvent> {
59        let mut events = vec![];
60        for test in self.uncommitted_tests.drain(..) {
61            events.push(FrameUpdateEvent::HitTestSourceAdded(test.id));
62            self.tests.push(test);
63        }
64        events
65    }
66
67    pub fn tests(&self) -> &[HitTestSource] {
68        &self.tests
69    }
70
71    pub fn cancel_hit_test(&mut self, id: HitTestId) {
72        self.tests.retain(|s| s.id != id);
73        self.uncommitted_tests.retain(|s| s.id != id);
74    }
75}
76
77#[inline]
78/// Construct a projection matrix given the four angles from the center for the faces of the viewing frustum
79pub fn fov_to_projection_matrix<T, U>(
80    left: f32,
81    right: f32,
82    top: f32,
83    bottom: f32,
84    clip_planes: ClipPlanes,
85) -> Transform3D<f32, T, U> {
86    let near = clip_planes.near;
87    // XXXManishearth deal with infinite planes
88    let left = left.tan() * near;
89    let right = right.tan() * near;
90    let top = top.tan() * near;
91    let bottom = bottom.tan() * near;
92
93    frustum_to_projection_matrix(left, right, top, bottom, clip_planes)
94}
95
96#[inline]
97/// Construct matrix given the actual extent of the viewing frustum on the near plane
98pub fn frustum_to_projection_matrix<T, U>(
99    left: f32,
100    right: f32,
101    top: f32,
102    bottom: f32,
103    clip_planes: ClipPlanes,
104) -> Transform3D<f32, T, U> {
105    let near = clip_planes.near;
106    let far = clip_planes.far;
107
108    let w = right - left;
109    let h = top - bottom;
110    let d = far - near;
111
112    // Column-major order
113    Transform3D::new(
114        2. * near / w,
115        0.,
116        0.,
117        0.,
118        0.,
119        2. * near / h,
120        0.,
121        0.,
122        (right + left) / w,
123        (top + bottom) / h,
124        -(far + near) / d,
125        -1.,
126        0.,
127        0.,
128        -2. * far * near / d,
129        0.,
130    )
131}