1use super::{Mat4, Vec3, Vec4, EPSILON};
21
22#[derive(Debug, Clone, Copy, PartialEq)]
28#[repr(C)]
29pub struct Aabb {
30 pub min: Vec3,
32 pub max: Vec3,
34}
35
36impl Aabb {
37 pub const INVALID: Self = Self {
42 min: Vec3::new(f32::INFINITY, f32::INFINITY, f32::INFINITY),
43 max: Vec3::new(f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY),
44 };
45
46 #[inline]
52 pub fn from_min_max(min_pt: Vec3, max_pt: Vec3) -> Self {
53 Self {
54 min: Vec3::new(
55 min_pt.x.min(max_pt.x),
56 min_pt.y.min(max_pt.y),
57 min_pt.z.min(max_pt.z),
58 ),
59 max: Vec3::new(
60 min_pt.x.max(max_pt.x),
61 min_pt.y.max(max_pt.y),
62 min_pt.z.max(max_pt.z),
63 ),
64 }
65 }
66
67 #[inline]
72 pub fn from_center_half_extents(center: Vec3, half_extents: Vec3) -> Self {
73 let safe_half_extents = half_extents.abs();
74 Self {
75 min: center - safe_half_extents,
76 max: center + safe_half_extents,
77 }
78 }
79
80 #[inline]
82 pub fn from_half_extents(half_extents: Vec3) -> Self {
83 let safe_half_extents = half_extents.abs();
84 Self {
85 min: -safe_half_extents,
86 max: safe_half_extents,
87 }
88 }
89
90 #[inline]
92 pub fn from_point(point: Vec3) -> Self {
93 Self {
94 min: point,
95 max: point,
96 }
97 }
98
99 pub fn from_points(points: &[Vec3]) -> Option<Self> {
105 if points.is_empty() {
106 return None;
107 }
108
109 let mut min_pt = points[0];
110 let mut max_pt = points[0];
111
112 for point in points.iter().skip(1) {
113 min_pt.x = min_pt.x.min(point.x);
114 min_pt.y = min_pt.y.min(point.y);
115 min_pt.z = min_pt.z.min(point.z);
116
117 max_pt.x = max_pt.x.max(point.x);
118 max_pt.y = max_pt.y.max(point.y);
119 max_pt.z = max_pt.z.max(point.z);
120 }
121
122 Some(Self {
123 min: min_pt,
124 max: max_pt,
125 })
126 }
127
128 #[inline]
130 pub fn center(&self) -> Vec3 {
131 (self.min + self.max) * 0.5
132 }
133
134 #[inline]
136 pub fn half_extents(&self) -> Vec3 {
137 (self.max - self.min) * 0.5
138 }
139
140 #[inline]
142 pub fn size(&self) -> Vec3 {
143 self.max - self.min
144 }
145
146 #[inline]
149 pub fn is_valid(&self) -> bool {
150 self.min.x <= self.max.x && self.min.y <= self.max.y && self.min.z <= self.max.z
151 }
152
153 #[inline]
155 pub fn contains_point(&self, point: Vec3) -> bool {
156 point.x >= self.min.x
157 && point.x <= self.max.x
158 && point.y >= self.min.y
159 && point.y <= self.max.y
160 && point.z >= self.min.z
161 && point.z <= self.max.z
162 }
163
164 #[inline]
169 pub fn intersects_aabb(&self, other: &Aabb) -> bool {
170 (self.min.x <= other.max.x && self.max.x >= other.min.x)
171 && (self.min.y <= other.max.y && self.max.y >= other.min.y)
172 && (self.min.z <= other.max.z && self.max.z >= other.min.z)
173 }
174
175 #[inline]
177 pub fn merge(&self, other: &Aabb) -> Self {
178 Self {
179 min: Vec3::new(
180 self.min.x.min(other.min.x),
181 self.min.y.min(other.min.y),
182 self.min.z.min(other.min.z),
183 ),
184 max: Vec3::new(
185 self.max.x.max(other.max.x),
186 self.max.y.max(other.max.y),
187 self.max.z.max(other.max.z),
188 ),
189 }
190 }
191
192 #[inline]
194 pub fn merged_with_point(&self, point: Vec3) -> Self {
195 Self {
196 min: Vec3::new(
197 self.min.x.min(point.x),
198 self.min.y.min(point.y),
199 self.min.z.min(point.z),
200 ),
201 max: Vec3::new(
202 self.max.x.max(point.x),
203 self.max.y.max(point.y),
204 self.max.z.max(point.z),
205 ),
206 }
207 }
208
209 pub fn transform(&self, matrix: &Mat4) -> Self {
220 let center = self.center();
221 let half_extents = self.half_extents();
222 let transformed_center_v4 = *matrix * Vec4::from_vec3(center, 1.0);
223
224 let transformed_center = if (transformed_center_v4.w - 1.0).abs() > EPSILON
225 && transformed_center_v4.w.abs() > EPSILON
226 {
227 transformed_center_v4.truncate() / transformed_center_v4.w
228 } else {
229 transformed_center_v4.truncate()
230 };
231
232 let x_abs_col = Vec3::new(
233 matrix.cols[0][0].abs(),
234 matrix.cols[0][1].abs(),
235 matrix.cols[0][2].abs(),
236 );
237 let y_abs_col = Vec3::new(
238 matrix.cols[1][0].abs(),
239 matrix.cols[1][1].abs(),
240 matrix.cols[1][2].abs(),
241 );
242 let z_abs_col = Vec3::new(
243 matrix.cols[2][0].abs(),
244 matrix.cols[2][1].abs(),
245 matrix.cols[2][2].abs(),
246 );
247
248 let new_half_extents =
249 x_abs_col * half_extents.x + y_abs_col * half_extents.y + z_abs_col * half_extents.z;
250
251 Aabb::from_center_half_extents(transformed_center, new_half_extents)
252 }
253
254 #[inline]
258 pub fn surface_area(&self) -> f32 {
259 let d = self.max - self.min;
260 2.0 * (d.x * d.y + d.y * d.z + d.z * d.x)
261 }
262
263 #[inline]
265 pub fn contains_aabb(&self, other: &Aabb) -> bool {
266 self.min.x <= other.min.x
267 && self.max.x >= other.max.x
268 && self.min.y <= other.min.y
269 && self.max.y >= other.max.y
270 && self.min.z <= other.min.z
271 && self.max.z >= other.max.z
272 }
273
274 pub fn intersect_ray(&self, origin: Vec3, inv_dir: Vec3) -> Option<f32> {
279 let t1 = (self.min.x - origin.x) * inv_dir.x;
280 let t2 = (self.max.x - origin.x) * inv_dir.x;
281 let t3 = (self.min.y - origin.y) * inv_dir.y;
282 let t4 = (self.max.y - origin.y) * inv_dir.y;
283 let t5 = (self.min.z - origin.z) * inv_dir.z;
284 let t6 = (self.max.z - origin.z) * inv_dir.z;
285
286 let tmin = t1.min(t2).max(t3.min(t4)).max(t5.min(t6));
287 let tmax = t1.max(t2).min(t3.max(t4)).min(t5.max(t6));
288
289 if tmax < 0.0 || tmin > tmax {
290 return None;
291 }
292
293 Some(tmin)
294 }
295}
296
297impl Default for Aabb {
298 #[inline]
300 fn default() -> Self {
301 Self::INVALID
302 }
303}
304
305#[cfg(test)]
307mod tests {
308 use super::*;
309 use crate::math::{approx_eq, matrix::Mat4, vector::Vec3}; use std::f32::consts::PI;
311
312 fn vec3_approx_eq(a: Vec3, b: Vec3) -> bool {
313 approx_eq(a.x, b.x) && approx_eq(a.y, b.y) && approx_eq(a.z, b.z)
314 }
315
316 fn aabb_approx_eq(a: Aabb, b: Aabb) -> bool {
318 vec3_approx_eq(a.min, b.min) && vec3_approx_eq(a.max, b.max)
319 }
320
321 #[test]
322 fn test_aabb_from_min_max() {
323 let aabb = Aabb::from_min_max(Vec3::new(1.0, 2.0, 3.0), Vec3::new(4.0, 5.0, 6.0));
324 assert_eq!(aabb.min, Vec3::new(1.0, 2.0, 3.0));
325 assert_eq!(aabb.max, Vec3::new(4.0, 5.0, 6.0));
326
327 let aabb_swapped = Aabb::from_min_max(Vec3::new(4.0, 5.0, 6.0), Vec3::new(1.0, 2.0, 3.0));
329 assert_eq!(aabb_swapped.min, Vec3::new(1.0, 2.0, 3.0));
330 assert_eq!(aabb_swapped.max, Vec3::new(4.0, 5.0, 6.0));
331 }
332
333 #[test]
334 fn test_aabb_from_center_half_extents() {
335 let center = Vec3::new(10.0, 20.0, 30.0);
336 let half_extents = Vec3::new(1.0, 2.0, 3.0);
337 let aabb = Aabb::from_center_half_extents(center, half_extents);
338
339 assert_eq!(aabb.min, Vec3::new(9.0, 18.0, 27.0));
340 assert_eq!(aabb.max, Vec3::new(11.0, 22.0, 33.0));
341 assert!(aabb_approx_eq(aabb, Aabb::from_min_max(aabb.min, aabb.max)));
342 }
343
344 #[test]
345 fn test_aabb_from_point() {
346 let p = Vec3::new(5.0, 6.0, 7.0);
347 let aabb = Aabb::from_point(p);
348
349 assert_eq!(aabb.min, p);
350 assert_eq!(aabb.max, p);
351 assert!(aabb.is_valid());
352 }
353
354 #[test]
355 fn test_aabb_from_points() {
356 assert!(Aabb::from_points(&[]).is_none());
357
358 let points = [
359 Vec3::new(1.0, 5.0, -1.0),
360 Vec3::new(0.0, 2.0, 3.0),
361 Vec3::new(4.0, 8.0, 0.0),
362 ];
363 let aabb = Aabb::from_points(&points).unwrap();
364
365 assert_eq!(aabb.min, Vec3::new(0.0, 2.0, -1.0));
366 assert_eq!(aabb.max, Vec3::new(4.0, 8.0, 3.0));
367 }
368
369 #[test]
370 fn test_aabb_utils() {
371 let aabb = Aabb::from_min_max(Vec3::new(-1.0, 0.0, 1.0), Vec3::new(3.0, 2.0, 5.0));
372
373 assert!(vec3_approx_eq(aabb.center(), Vec3::new(1.0, 1.0, 3.0)));
374 assert!(vec3_approx_eq(aabb.size(), Vec3::new(4.0, 2.0, 4.0)));
375 assert!(vec3_approx_eq(
376 aabb.half_extents(),
377 Vec3::new(2.0, 1.0, 2.0)
378 ));
379 assert!(aabb.is_valid());
380 assert!(!Aabb::INVALID.is_valid());
381 assert!(Aabb::from_point(Vec3::ZERO).is_valid());
382 }
383
384 #[test]
385 fn test_aabb_contains_point() {
386 let aabb = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(1.0, 1.0, 1.0));
387 assert!(aabb.contains_point(Vec3::new(0.5, 0.5, 0.5)));
389
390 assert!(aabb.contains_point(Vec3::new(0.0, 0.5, 0.5)));
392 assert!(aabb.contains_point(Vec3::new(1.0, 0.5, 0.5)));
393 assert!(aabb.contains_point(Vec3::new(0.5, 0.0, 0.5)));
394 assert!(aabb.contains_point(Vec3::new(0.5, 1.0, 0.5)));
395 assert!(aabb.contains_point(Vec3::new(0.5, 0.5, 0.0)));
396 assert!(aabb.contains_point(Vec3::new(0.5, 0.5, 1.0)));
397 assert!(aabb.contains_point(Vec3::new(0.0, 0.0, 0.0)));
398 assert!(aabb.contains_point(Vec3::new(1.0, 1.0, 1.0)));
399
400 assert!(!aabb.contains_point(Vec3::new(1.1, 0.5, 0.5)));
402 assert!(!aabb.contains_point(Vec3::new(-0.1, 0.5, 0.5)));
403 assert!(!aabb.contains_point(Vec3::new(0.5, 1.1, 0.5)));
404 assert!(!aabb.contains_point(Vec3::new(0.5, -0.1, 0.5)));
405 assert!(!aabb.contains_point(Vec3::new(0.5, 0.5, 1.1)));
406 assert!(!aabb.contains_point(Vec3::new(0.5, 0.5, -0.1)));
407 }
408
409 #[test]
410 fn test_aabb_intersects_aabb() {
411 let aabb1 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 2.0, 2.0));
412
413 let aabb2 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 2.0, 2.0));
415 assert!(aabb1.intersects_aabb(&aabb2));
416
417 let aabb3 = Aabb::from_min_max(Vec3::new(1.0, 1.0, 1.0), Vec3::new(3.0, 3.0, 3.0));
419 assert!(aabb1.intersects_aabb(&aabb3));
420 assert!(aabb3.intersects_aabb(&aabb1));
421
422 let aabb4 = Aabb::from_min_max(Vec3::new(2.0, 0.0, 0.0), Vec3::new(3.0, 2.0, 2.0));
424 assert!(aabb1.intersects_aabb(&aabb4));
425 assert!(aabb4.intersects_aabb(&aabb1));
426
427 let aabb5 = Aabb::from_min_max(Vec3::new(0.5, 0.5, 0.5), Vec3::new(1.5, 1.5, 1.5));
429 assert!(aabb1.intersects_aabb(&aabb5));
430 assert!(aabb5.intersects_aabb(&aabb1));
431
432 let aabb6 = Aabb::from_min_max(Vec3::new(2.1, 0.0, 0.0), Vec3::new(3.0, 2.0, 2.0));
434 assert!(!aabb1.intersects_aabb(&aabb6));
435 assert!(!aabb6.intersects_aabb(&aabb1));
436
437 let aabb7 = Aabb::from_min_max(Vec3::new(0.0, 2.1, 0.0), Vec3::new(2.0, 3.0, 2.0));
439 assert!(!aabb1.intersects_aabb(&aabb7));
440 assert!(!aabb7.intersects_aabb(&aabb1));
441
442 let aabb8 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 2.1), Vec3::new(2.0, 2.0, 3.0));
444 assert!(!aabb1.intersects_aabb(&aabb8));
445 assert!(!aabb8.intersects_aabb(&aabb1));
446 }
447
448 #[test]
449 fn test_aabb_contains_aabb() {
450 let aabb1 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(10.0, 10.0, 10.0));
451
452 let aabb2 = Aabb::from_min_max(Vec3::new(2.0, 2.0, 2.0), Vec3::new(8.0, 8.0, 8.0));
454 assert!(aabb1.contains_aabb(&aabb2));
455 assert!(!aabb2.contains_aabb(&aabb1));
456
457 let aabb3 = Aabb::from_min_max(Vec3::new(5.0, 5.0, 5.0), Vec3::new(15.0, 15.0, 15.0));
459 assert!(aabb1.intersects_aabb(&aabb3));
460 assert!(!aabb1.contains_aabb(&aabb3));
461
462 assert!(aabb1.contains_aabb(&aabb1));
464 }
465
466 #[test]
467 fn test_aabb_merge() {
468 let aabb1 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(1.0, 1.0, 1.0));
469 let aabb2 = Aabb::from_min_max(Vec3::new(0.5, 0.5, 0.5), Vec3::new(1.5, 1.5, 1.5));
470 let merged_aabb = aabb1.merge(&aabb2);
471
472 assert_eq!(merged_aabb.min, Vec3::new(0.0, 0.0, 0.0));
473 assert_eq!(merged_aabb.max, Vec3::new(1.5, 1.5, 1.5));
474
475 let point = Vec3::new(-1.0, 0.5, 2.0);
476 let merged_point = aabb1.merged_with_point(point);
477
478 assert_eq!(merged_point.min, Vec3::new(-1.0, 0.0, 0.0));
479 assert_eq!(merged_point.max, Vec3::new(1.0, 1.0, 2.0));
480
481 let merged_with_invalid = Aabb::INVALID.merge(&aabb1);
483 assert!(aabb_approx_eq(merged_with_invalid, aabb1));
484
485 let merged_with_invalid_pt = Aabb::INVALID.merged_with_point(point);
486 assert!(aabb_approx_eq(
487 merged_with_invalid_pt,
488 Aabb::from_point(point)
489 ));
490 }
491
492 #[test]
493 fn test_aabb_transform() {
494 let aabb = Aabb::from_min_max(Vec3::new(-1.0, -1.0, -1.0), Vec3::new(1.0, 1.0, 1.0)); let matrix = Mat4::from_translation(Vec3::new(10.0, 0.0, 0.0)); let transformed_aabb = aabb.transform(&matrix);
497 let expected_aabb =
498 Aabb::from_min_max(Vec3::new(9.0, -1.0, -1.0), Vec3::new(11.0, 1.0, 1.0));
499
500 assert!(aabb_approx_eq(transformed_aabb, expected_aabb));
501
502 let matrix_rot = Mat4::from_rotation_y(PI / 4.0); let transformed_rot_aabb = aabb.transform(&matrix_rot);
505
506 let sqrt2 = 2.0f32.sqrt();
509
510 assert!(approx_eq(transformed_rot_aabb.min.x, -sqrt2));
511 assert!(approx_eq(transformed_rot_aabb.max.x, sqrt2));
512 assert!(approx_eq(transformed_rot_aabb.min.y, -1.0)); assert!(approx_eq(transformed_rot_aabb.max.y, 1.0));
514 assert!(approx_eq(transformed_rot_aabb.min.z, -sqrt2));
515 assert!(approx_eq(transformed_rot_aabb.max.z, sqrt2));
516
517 let matrix_scale = Mat4::from_scale(Vec3::new(2.0, 1.0, 0.5));
519 let transformed_scale_aabb = aabb.transform(&matrix_scale);
520 let expected_scale_aabb =
521 Aabb::from_min_max(Vec3::new(-2.0, -1.0, -0.5), Vec3::new(2.0, 1.0, 0.5));
522
523 assert!(aabb_approx_eq(transformed_scale_aabb, expected_scale_aabb));
524 }
525}