rg3d_core/math/
frustum.rs

1use crate::{
2    algebra::{Matrix4, Vector3},
3    math::{aabb::AxisAlignedBoundingBox, plane::Plane},
4    visitor::{Visit, VisitResult, Visitor},
5};
6use nalgebra::Point3;
7
8#[derive(Copy, Clone, Debug, PartialEq)]
9pub struct Frustum {
10    /// 0 - left, 1 - right, 2 - top, 3 - bottom, 4 - far, 5 - near
11    planes: [Plane; 6],
12}
13
14impl Default for Frustum {
15    #[inline]
16    fn default() -> Self {
17        Self::from(Matrix4::new_perspective(
18            1.0,
19            std::f32::consts::FRAC_PI_2,
20            0.01,
21            1024.0,
22        ))
23        .unwrap()
24    }
25}
26
27impl Frustum {
28    #[inline]
29    pub fn from(m: Matrix4<f32>) -> Option<Self> {
30        Some(Self {
31            planes: [
32                Plane::from_abcd(m[3] + m[0], m[7] + m[4], m[11] + m[8], m[15] + m[12])?,
33                Plane::from_abcd(m[3] - m[0], m[7] - m[4], m[11] - m[8], m[15] - m[12])?,
34                Plane::from_abcd(m[3] - m[1], m[7] - m[5], m[11] - m[9], m[15] - m[13])?,
35                Plane::from_abcd(m[3] + m[1], m[7] + m[5], m[11] + m[9], m[15] + m[13])?,
36                Plane::from_abcd(m[3] - m[2], m[7] - m[6], m[11] - m[10], m[15] - m[14])?,
37                Plane::from_abcd(m[3] + m[2], m[7] + m[6], m[11] + m[10], m[15] + m[14])?,
38            ],
39        })
40    }
41
42    #[inline]
43    pub fn left(&self) -> &Plane {
44        self.planes.get(0).unwrap()
45    }
46
47    #[inline]
48    pub fn right(&self) -> &Plane {
49        self.planes.get(1).unwrap()
50    }
51
52    #[inline]
53    pub fn top(&self) -> &Plane {
54        self.planes.get(2).unwrap()
55    }
56
57    #[inline]
58    pub fn bottom(&self) -> &Plane {
59        self.planes.get(3).unwrap()
60    }
61
62    #[inline]
63    pub fn far(&self) -> &Plane {
64        self.planes.get(4).unwrap()
65    }
66
67    #[inline]
68    pub fn near(&self) -> &Plane {
69        self.planes.get(5).unwrap()
70    }
71
72    #[inline]
73    pub fn planes(&self) -> &[Plane] {
74        &self.planes
75    }
76
77    #[inline]
78    pub fn left_top_front_corner(&self) -> Vector3<f32> {
79        self.left().intersection_point(self.top(), self.far())
80    }
81
82    #[inline]
83    pub fn left_bottom_front_corner(&self) -> Vector3<f32> {
84        self.left().intersection_point(self.bottom(), self.far())
85    }
86
87    #[inline]
88    pub fn right_bottom_front_corner(&self) -> Vector3<f32> {
89        self.right().intersection_point(self.bottom(), self.far())
90    }
91
92    #[inline]
93    pub fn right_top_front_corner(&self) -> Vector3<f32> {
94        self.right().intersection_point(self.top(), self.far())
95    }
96
97    #[inline]
98    pub fn left_top_back_corner(&self) -> Vector3<f32> {
99        self.left().intersection_point(self.top(), self.near())
100    }
101
102    #[inline]
103    pub fn left_bottom_back_corner(&self) -> Vector3<f32> {
104        self.left().intersection_point(self.bottom(), self.near())
105    }
106
107    #[inline]
108    pub fn right_bottom_back_corner(&self) -> Vector3<f32> {
109        self.right().intersection_point(self.bottom(), self.near())
110    }
111
112    #[inline]
113    pub fn right_top_back_corner(&self) -> Vector3<f32> {
114        self.right().intersection_point(self.top(), self.near())
115    }
116
117    #[inline]
118    pub fn corners(&self) -> [Vector3<f32>; 8] {
119        [
120            self.left_top_front_corner(),
121            self.left_bottom_front_corner(),
122            self.right_bottom_front_corner(),
123            self.right_top_front_corner(),
124            self.left_top_back_corner(),
125            self.left_bottom_back_corner(),
126            self.right_bottom_back_corner(),
127            self.right_top_back_corner(),
128        ]
129    }
130
131    #[inline]
132    pub fn is_intersects_point_cloud(&self, points: &[Vector3<f32>]) -> bool {
133        for plane in self.planes.iter() {
134            let mut back_points = 0;
135            for point in points {
136                if plane.dot(point) <= 0.0 {
137                    back_points += 1;
138                    if back_points >= points.len() {
139                        // All points are behind current plane.
140                        return false;
141                    }
142                }
143            }
144        }
145        true
146    }
147
148    #[inline]
149    pub fn is_intersects_aabb(&self, aabb: &AxisAlignedBoundingBox) -> bool {
150        let corners = [
151            Vector3::new(aabb.min.x, aabb.min.y, aabb.min.z),
152            Vector3::new(aabb.min.x, aabb.min.y, aabb.max.z),
153            Vector3::new(aabb.max.x, aabb.min.y, aabb.max.z),
154            Vector3::new(aabb.max.x, aabb.min.y, aabb.min.z),
155            Vector3::new(aabb.min.x, aabb.max.y, aabb.min.z),
156            Vector3::new(aabb.min.x, aabb.max.y, aabb.max.z),
157            Vector3::new(aabb.max.x, aabb.max.y, aabb.max.z),
158            Vector3::new(aabb.max.x, aabb.max.y, aabb.min.z),
159        ];
160
161        self.is_intersects_point_cloud(&corners)
162    }
163
164    #[inline]
165    pub fn is_intersects_aabb_offset(
166        &self,
167        aabb: &AxisAlignedBoundingBox,
168        offset: Vector3<f32>,
169    ) -> bool {
170        let corners = [
171            Vector3::new(aabb.min.x, aabb.min.y, aabb.min.z) + offset,
172            Vector3::new(aabb.min.x, aabb.min.y, aabb.max.z) + offset,
173            Vector3::new(aabb.max.x, aabb.min.y, aabb.max.z) + offset,
174            Vector3::new(aabb.max.x, aabb.min.y, aabb.min.z) + offset,
175            Vector3::new(aabb.min.x, aabb.max.y, aabb.min.z) + offset,
176            Vector3::new(aabb.min.x, aabb.max.y, aabb.max.z) + offset,
177            Vector3::new(aabb.max.x, aabb.max.y, aabb.max.z) + offset,
178            Vector3::new(aabb.max.x, aabb.max.y, aabb.min.z) + offset,
179        ];
180
181        self.is_intersects_point_cloud(&corners)
182    }
183
184    #[inline]
185    pub fn is_intersects_aabb_transform(
186        &self,
187        aabb: &AxisAlignedBoundingBox,
188        transform: &Matrix4<f32>,
189    ) -> bool {
190        if self.is_contains_point(
191            transform
192                .transform_point(&Point3::from(aabb.center()))
193                .coords,
194        ) {
195            return true;
196        }
197
198        let corners = [
199            transform
200                .transform_point(&Point3::new(aabb.min.x, aabb.min.y, aabb.min.z))
201                .coords,
202            transform
203                .transform_point(&Point3::new(aabb.min.x, aabb.min.y, aabb.max.z))
204                .coords,
205            transform
206                .transform_point(&Point3::new(aabb.max.x, aabb.min.y, aabb.max.z))
207                .coords,
208            transform
209                .transform_point(&Point3::new(aabb.max.x, aabb.min.y, aabb.min.z))
210                .coords,
211            transform
212                .transform_point(&Point3::new(aabb.min.x, aabb.max.y, aabb.min.z))
213                .coords,
214            transform
215                .transform_point(&Point3::new(aabb.min.x, aabb.max.y, aabb.max.z))
216                .coords,
217            transform
218                .transform_point(&Point3::new(aabb.max.x, aabb.max.y, aabb.max.z))
219                .coords,
220            transform
221                .transform_point(&Point3::new(aabb.max.x, aabb.max.y, aabb.min.z))
222                .coords,
223        ];
224
225        self.is_intersects_point_cloud(&corners)
226    }
227
228    #[inline]
229    pub fn is_contains_point(&self, pt: Vector3<f32>) -> bool {
230        for plane in self.planes.iter() {
231            if plane.dot(&pt) <= 0.0 {
232                return false;
233            }
234        }
235        true
236    }
237
238    #[inline]
239    pub fn is_intersects_sphere(&self, p: Vector3<f32>, r: f32) -> bool {
240        for plane in self.planes.iter() {
241            let d = plane.dot(&p);
242            if d < -r {
243                return false;
244            }
245            if d.abs() < r {
246                return true;
247            }
248        }
249        true
250    }
251}
252
253impl Visit for Frustum {
254    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
255        visitor.enter_region(name)?;
256
257        self.planes[0].visit("Left", visitor)?;
258        self.planes[1].visit("Right", visitor)?;
259        self.planes[2].visit("Top", visitor)?;
260        self.planes[3].visit("Bottom", visitor)?;
261        self.planes[4].visit("Far", visitor)?;
262        self.planes[5].visit("Near", visitor)?;
263
264        visitor.leave_region()
265    }
266}