|
1 | 1 | mod primitive_impls;
|
2 | 2 |
|
3 |
| -use super::BoundingVolume; |
| 3 | +use super::{BoundingVolume, IntersectsVolume}; |
4 | 4 | use crate::prelude::{Quat, Vec3};
|
5 | 5 |
|
6 | 6 | /// Computes the geometric center of the given set of points.
|
@@ -74,6 +74,16 @@ impl Aabb3d {
|
74 | 74 | let radius = self.min.distance(self.max) / 2.0;
|
75 | 75 | BoundingSphere::new(self.center(), radius)
|
76 | 76 | }
|
| 77 | + |
| 78 | + /// Finds the point on the AABB that is closest to the given `point`. |
| 79 | + /// |
| 80 | + /// If the point is outside the AABB, the returned point will be on the surface of the AABB. |
| 81 | + /// Otherwise, it will be inside the AABB and returned as is. |
| 82 | + #[inline(always)] |
| 83 | + pub fn closest_point(&self, point: Vec3) -> Vec3 { |
| 84 | + // Clamp point coordinates to the AABB |
| 85 | + point.clamp(self.min, self.max) |
| 86 | + } |
77 | 87 | }
|
78 | 88 |
|
79 | 89 | impl BoundingVolume for Aabb3d {
|
@@ -135,10 +145,33 @@ impl BoundingVolume for Aabb3d {
|
135 | 145 | }
|
136 | 146 | }
|
137 | 147 |
|
| 148 | +impl IntersectsVolume<Self> for Aabb3d { |
| 149 | + #[inline(always)] |
| 150 | + fn intersects(&self, other: &Self) -> bool { |
| 151 | + let x_overlaps = self.min.x <= other.max.x && self.max.x >= other.min.x; |
| 152 | + let y_overlaps = self.min.y <= other.max.y && self.max.y >= other.min.y; |
| 153 | + let z_overlaps = self.min.z <= other.max.z && self.max.z >= other.min.z; |
| 154 | + x_overlaps && y_overlaps && z_overlaps |
| 155 | + } |
| 156 | +} |
| 157 | + |
| 158 | +impl IntersectsVolume<BoundingSphere> for Aabb3d { |
| 159 | + #[inline(always)] |
| 160 | + fn intersects(&self, sphere: &BoundingSphere) -> bool { |
| 161 | + let closest_point = self.closest_point(sphere.center); |
| 162 | + let distance_squared = sphere.center.distance_squared(closest_point); |
| 163 | + let radius_squared = sphere.radius().powi(2); |
| 164 | + distance_squared <= radius_squared |
| 165 | + } |
| 166 | +} |
| 167 | + |
138 | 168 | #[cfg(test)]
|
139 | 169 | mod aabb3d_tests {
|
140 | 170 | use super::Aabb3d;
|
141 |
| - use crate::{bounding::BoundingVolume, Vec3}; |
| 171 | + use crate::{ |
| 172 | + bounding::{BoundingSphere, BoundingVolume, IntersectsVolume}, |
| 173 | + Vec3, |
| 174 | + }; |
142 | 175 |
|
143 | 176 | #[test]
|
144 | 177 | fn center() {
|
@@ -239,6 +272,53 @@ mod aabb3d_tests {
|
239 | 272 | assert!(a.contains(&shrunk));
|
240 | 273 | assert!(!shrunk.contains(&a));
|
241 | 274 | }
|
| 275 | + |
| 276 | + #[test] |
| 277 | + fn closest_point() { |
| 278 | + let aabb = Aabb3d { |
| 279 | + min: Vec3::NEG_ONE, |
| 280 | + max: Vec3::ONE, |
| 281 | + }; |
| 282 | + assert_eq!(aabb.closest_point(Vec3::X * 10.0), Vec3::X); |
| 283 | + assert_eq!(aabb.closest_point(Vec3::NEG_ONE * 10.0), Vec3::NEG_ONE); |
| 284 | + assert_eq!( |
| 285 | + aabb.closest_point(Vec3::new(0.25, 0.1, 0.3)), |
| 286 | + Vec3::new(0.25, 0.1, 0.3) |
| 287 | + ); |
| 288 | + } |
| 289 | + |
| 290 | + #[test] |
| 291 | + fn intersect_aabb() { |
| 292 | + let aabb = Aabb3d { |
| 293 | + min: Vec3::NEG_ONE, |
| 294 | + max: Vec3::ONE, |
| 295 | + }; |
| 296 | + assert!(aabb.intersects(&aabb)); |
| 297 | + assert!(aabb.intersects(&Aabb3d { |
| 298 | + min: Vec3::splat(0.5), |
| 299 | + max: Vec3::splat(2.0), |
| 300 | + })); |
| 301 | + assert!(aabb.intersects(&Aabb3d { |
| 302 | + min: Vec3::splat(-2.0), |
| 303 | + max: Vec3::splat(-0.5), |
| 304 | + })); |
| 305 | + assert!(!aabb.intersects(&Aabb3d { |
| 306 | + min: Vec3::new(1.1, 0.0, 0.0), |
| 307 | + max: Vec3::new(2.0, 0.5, 0.25), |
| 308 | + })); |
| 309 | + } |
| 310 | + |
| 311 | + #[test] |
| 312 | + fn intersect_bounding_sphere() { |
| 313 | + let aabb = Aabb3d { |
| 314 | + min: Vec3::NEG_ONE, |
| 315 | + max: Vec3::ONE, |
| 316 | + }; |
| 317 | + assert!(aabb.intersects(&BoundingSphere::new(Vec3::ZERO, 1.0))); |
| 318 | + assert!(aabb.intersects(&BoundingSphere::new(Vec3::ONE * 1.5, 1.0))); |
| 319 | + assert!(aabb.intersects(&BoundingSphere::new(Vec3::NEG_ONE * 1.5, 1.0))); |
| 320 | + assert!(!aabb.intersects(&BoundingSphere::new(Vec3::ONE * 1.75, 1.0))); |
| 321 | + } |
242 | 322 | }
|
243 | 323 |
|
244 | 324 | use crate::primitives::Sphere;
|
@@ -296,6 +376,15 @@ impl BoundingSphere {
|
296 | 376 | max: self.center + Vec3::splat(self.radius()),
|
297 | 377 | }
|
298 | 378 | }
|
| 379 | + |
| 380 | + /// Finds the point on the bounding sphere that is closest to the given `point`. |
| 381 | + /// |
| 382 | + /// If the point is outside the sphere, the returned point will be on the surface of the sphere. |
| 383 | + /// Otherwise, it will be inside the sphere and returned as is. |
| 384 | + #[inline(always)] |
| 385 | + pub fn closest_point(&self, point: Vec3) -> Vec3 { |
| 386 | + self.sphere.closest_point(point - self.center) + self.center |
| 387 | + } |
299 | 388 | }
|
300 | 389 |
|
301 | 390 | impl BoundingVolume for BoundingSphere {
|
@@ -364,10 +453,29 @@ impl BoundingVolume for BoundingSphere {
|
364 | 453 | }
|
365 | 454 | }
|
366 | 455 |
|
| 456 | +impl IntersectsVolume<Self> for BoundingSphere { |
| 457 | + #[inline(always)] |
| 458 | + fn intersects(&self, other: &Self) -> bool { |
| 459 | + let center_distance_squared = self.center.distance_squared(other.center); |
| 460 | + let radius_sum_squared = (self.radius() + other.radius()).powi(2); |
| 461 | + center_distance_squared <= radius_sum_squared |
| 462 | + } |
| 463 | +} |
| 464 | + |
| 465 | +impl IntersectsVolume<Aabb3d> for BoundingSphere { |
| 466 | + #[inline(always)] |
| 467 | + fn intersects(&self, aabb: &Aabb3d) -> bool { |
| 468 | + aabb.intersects(self) |
| 469 | + } |
| 470 | +} |
| 471 | + |
367 | 472 | #[cfg(test)]
|
368 | 473 | mod bounding_sphere_tests {
|
369 | 474 | use super::BoundingSphere;
|
370 |
| - use crate::{bounding::BoundingVolume, Vec3}; |
| 475 | + use crate::{ |
| 476 | + bounding::{BoundingVolume, IntersectsVolume}, |
| 477 | + Vec3, |
| 478 | + }; |
371 | 479 |
|
372 | 480 | #[test]
|
373 | 481 | fn area() {
|
@@ -444,4 +552,27 @@ mod bounding_sphere_tests {
|
444 | 552 | assert!(a.contains(&shrunk));
|
445 | 553 | assert!(!shrunk.contains(&a));
|
446 | 554 | }
|
| 555 | + |
| 556 | + #[test] |
| 557 | + fn closest_point() { |
| 558 | + let sphere = BoundingSphere::new(Vec3::ZERO, 1.0); |
| 559 | + assert_eq!(sphere.closest_point(Vec3::X * 10.0), Vec3::X); |
| 560 | + assert_eq!( |
| 561 | + sphere.closest_point(Vec3::NEG_ONE * 10.0), |
| 562 | + Vec3::NEG_ONE.normalize() |
| 563 | + ); |
| 564 | + assert_eq!( |
| 565 | + sphere.closest_point(Vec3::new(0.25, 0.1, 0.3)), |
| 566 | + Vec3::new(0.25, 0.1, 0.3) |
| 567 | + ); |
| 568 | + } |
| 569 | + |
| 570 | + #[test] |
| 571 | + fn intersect_bounding_sphere() { |
| 572 | + let sphere = BoundingSphere::new(Vec3::ZERO, 1.0); |
| 573 | + assert!(sphere.intersects(&BoundingSphere::new(Vec3::ZERO, 1.0))); |
| 574 | + assert!(sphere.intersects(&BoundingSphere::new(Vec3::ONE * 1.1, 1.0))); |
| 575 | + assert!(sphere.intersects(&BoundingSphere::new(Vec3::NEG_ONE * 1.1, 1.0))); |
| 576 | + assert!(!sphere.intersects(&BoundingSphere::new(Vec3::ONE * 1.2, 1.0))); |
| 577 | + } |
447 | 578 | }
|
0 commit comments