use crate::{Mat4, Vec3};
#[cfg(test)]
#[path = "aabb_tests.rs"]
mod tests;
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Aabb {
pub min: Vec3,
pub max: Vec3,
}
impl Default for Aabb {
#[inline]
fn default() -> Self {
Self::EMPTY
}
}
impl Aabb {
pub const EMPTY: Self = Self {
min: Vec3::splat(f32::INFINITY),
max: Vec3::splat(f32::NEG_INFINITY),
};
#[must_use]
#[inline]
pub const fn new(min: Vec3, max: Vec3) -> Self {
Self { min, max }
}
#[must_use]
pub fn from_points(points: impl IntoIterator<Item = Vec3>) -> Self {
let mut aabb = Self::EMPTY;
for p in points {
aabb.extend(p);
}
aabb
}
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool {
self.min.x > self.max.x
}
#[inline]
pub fn extend(&mut self, point: Vec3) {
if point.is_finite() {
self.min = self.min.min(point);
self.max = self.max.max(point);
}
}
#[inline]
pub fn extend_sphere(&mut self, center: Vec3, radius: f32) {
if center.is_finite() && radius.is_finite() {
let r = Vec3::splat(radius.abs());
self.min = self.min.min(center - r);
self.max = self.max.max(center + r);
}
}
#[must_use]
#[inline]
pub fn union(&self, other: &Self) -> Self {
Self {
min: self.min.min(other.min),
max: self.max.max(other.max),
}
}
#[must_use]
#[inline]
pub fn overlaps(&self, other: &Self) -> bool {
!self.is_empty()
&& !other.is_empty()
&& self.min.x <= other.max.x
&& self.min.y <= other.max.y
&& self.min.z <= other.max.z
&& other.min.x <= self.max.x
&& other.min.y <= self.max.y
&& other.min.z <= self.max.z
}
#[must_use]
#[inline]
pub fn center(&self) -> Vec3 {
(self.min + self.max) * 0.5
}
#[must_use]
#[inline]
pub fn half_extents(&self) -> Vec3 {
(self.max - self.min) * 0.5
}
#[must_use]
#[inline]
pub fn corners(&self) -> [Vec3; 8] {
let (lo, hi) = (self.min, self.max);
[
Vec3::new(lo.x, lo.y, lo.z),
Vec3::new(hi.x, lo.y, lo.z),
Vec3::new(lo.x, hi.y, lo.z),
Vec3::new(hi.x, hi.y, lo.z),
Vec3::new(lo.x, lo.y, hi.z),
Vec3::new(hi.x, lo.y, hi.z),
Vec3::new(lo.x, hi.y, hi.z),
Vec3::new(hi.x, hi.y, hi.z),
]
}
#[must_use]
#[inline]
pub fn transform(&self, matrix: &Mat4) -> Self {
if self.is_empty() {
return Self::EMPTY;
}
let mut out = Self::EMPTY;
for corner in self.corners() {
out.extend(matrix.transform_point3(corner));
}
out
}
#[must_use]
#[inline]
pub fn ray_intersect(&self, origin: Vec3, inv_dir: Vec3) -> Option<(f32, f32)> {
let t0 = (self.min - origin) * inv_dir;
let t1 = (self.max - origin) * inv_dir;
let t_near = t0.min(t1).max_element();
let t_far = t0.max(t1).min_element();
if t_near <= t_far && t_far >= 0.0 {
Some((t_near.max(0.0), t_far))
} else {
None
}
}
#[must_use]
#[inline]
pub fn bounding_sphere(&self) -> BoundingSphere {
if self.is_empty() {
return BoundingSphere {
center: Vec3::ZERO,
radius: 0.0,
};
}
BoundingSphere {
center: self.center(),
radius: self.half_extents().length(),
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct BoundingSphere {
pub center: Vec3,
pub radius: f32,
}
impl BoundingSphere {
#[must_use]
pub fn from_points(points: &[Vec3]) -> Self {
let mut sum = glam::DVec3::ZERO;
let mut count = 0u32;
for p in points {
if p.is_finite() {
sum += glam::Vec3::from(*p).as_dvec3();
count += 1;
}
}
if count == 0 {
return Self {
center: Vec3::ZERO,
radius: 0.0,
};
}
let center = Vec3::from((sum / f64::from(count)).as_vec3());
let mut radius_sq = 0.0f32;
for p in points {
if p.is_finite() {
radius_sq = radius_sq.max(center.distance_squared(*p));
}
}
Self {
center,
radius: radius_sq.sqrt(),
}
}
}