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 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 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}