177 lines
4.8 KiB
Rust
177 lines
4.8 KiB
Rust
#![allow(dead_code)]
|
|
|
|
use std::{
|
|
iter::Iterator,
|
|
ops::{BitOr, BitOrAssign},
|
|
};
|
|
|
|
use crate::{
|
|
lerp::{lerp, lerp_slice, Lerp},
|
|
math::{fast_minf32, Matrix4x4, Point, Vector},
|
|
};
|
|
|
|
const BBOX_MAXT_ADJUST: f32 = 1.000_000_24;
|
|
|
|
/// A 3D axis-aligned bounding box.
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct BBox {
|
|
pub min: Point,
|
|
pub max: Point,
|
|
}
|
|
|
|
impl BBox {
|
|
/// Creates a degenerate BBox with +infinity min and -infinity max.
|
|
pub fn new() -> BBox {
|
|
BBox {
|
|
min: Point::new(std::f32::INFINITY, std::f32::INFINITY, std::f32::INFINITY),
|
|
max: Point::new(
|
|
std::f32::NEG_INFINITY,
|
|
std::f32::NEG_INFINITY,
|
|
std::f32::NEG_INFINITY,
|
|
),
|
|
}
|
|
}
|
|
|
|
/// Creates a BBox with min as the minimum extent and max as the maximum
|
|
/// extent.
|
|
pub fn from_points(min: Point, max: Point) -> BBox {
|
|
BBox { min: min, max: max }
|
|
}
|
|
|
|
// Returns whether the given ray intersects with the bbox.
|
|
pub fn intersect_ray(&self, orig: Point, dir_inv: Vector, max_t: f32) -> bool {
|
|
// Calculate slab intersections
|
|
let t1 = (self.min.co - orig.co) * dir_inv.co;
|
|
let t2 = (self.max.co - orig.co) * dir_inv.co;
|
|
|
|
// Find the far and near intersection
|
|
let mut far_t = t1.max(t2);
|
|
let mut near_t = t1.min(t2);
|
|
far_t.set_w(std::f32::INFINITY);
|
|
near_t.set_w(0.0);
|
|
let far_hit_t = fast_minf32(far_t.min_element() * BBOX_MAXT_ADJUST, max_t);
|
|
let near_hit_t = near_t.max_element();
|
|
|
|
// Did we hit?
|
|
near_hit_t <= far_hit_t
|
|
}
|
|
|
|
// Creates a new BBox transformed into a different space.
|
|
pub fn transformed(&self, xform: Matrix4x4) -> BBox {
|
|
// BBox corners
|
|
let vs = [
|
|
Point::new(self.min.x(), self.min.y(), self.min.z()),
|
|
Point::new(self.min.x(), self.min.y(), self.max.z()),
|
|
Point::new(self.min.x(), self.max.y(), self.min.z()),
|
|
Point::new(self.min.x(), self.max.y(), self.max.z()),
|
|
Point::new(self.max.x(), self.min.y(), self.min.z()),
|
|
Point::new(self.max.x(), self.min.y(), self.max.z()),
|
|
Point::new(self.max.x(), self.max.y(), self.min.z()),
|
|
Point::new(self.max.x(), self.max.y(), self.max.z()),
|
|
];
|
|
|
|
// Transform BBox corners and make new bbox
|
|
let mut b = BBox::new();
|
|
for v in &vs {
|
|
let v = *v * xform;
|
|
b.min = v.min(b.min);
|
|
b.max = v.max(b.max);
|
|
}
|
|
|
|
b
|
|
}
|
|
|
|
pub fn surface_area(&self) -> f32 {
|
|
let d = self.max - self.min;
|
|
((d.x() * d.y()) + (d.y() * d.z()) + (d.z() * d.x())) * 2.0
|
|
}
|
|
|
|
pub fn center(&self) -> Point {
|
|
self.min.lerp(self.max, 0.5)
|
|
}
|
|
|
|
pub fn diagonal(&self) -> f32 {
|
|
(self.max - self.min).length()
|
|
}
|
|
|
|
pub fn diagonal2(&self) -> f32 {
|
|
(self.max - self.min).length2()
|
|
}
|
|
}
|
|
|
|
/// Union of two `BBox`es.
|
|
impl BitOr for BBox {
|
|
type Output = BBox;
|
|
|
|
fn bitor(self, rhs: BBox) -> BBox {
|
|
BBox::from_points(
|
|
Point {
|
|
co: self.min.co.min(rhs.min.co),
|
|
},
|
|
Point {
|
|
co: self.max.co.max(rhs.max.co),
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
impl BitOrAssign for BBox {
|
|
fn bitor_assign(&mut self, rhs: BBox) {
|
|
*self = *self | rhs;
|
|
}
|
|
}
|
|
|
|
/// Expand `BBox` by a point.
|
|
impl BitOr<Point> for BBox {
|
|
type Output = BBox;
|
|
|
|
fn bitor(self, rhs: Point) -> BBox {
|
|
BBox::from_points(
|
|
Point {
|
|
co: self.min.co.min(rhs.co),
|
|
},
|
|
Point {
|
|
co: self.max.co.max(rhs.co),
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
impl BitOrAssign<Point> for BBox {
|
|
fn bitor_assign(&mut self, rhs: Point) {
|
|
*self = *self | rhs;
|
|
}
|
|
}
|
|
|
|
impl Lerp for BBox {
|
|
fn lerp(self, other: BBox, alpha: f32) -> BBox {
|
|
BBox {
|
|
min: lerp(self.min, other.min, alpha),
|
|
max: lerp(self.max, other.max, alpha),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn transform_bbox_slice_from(bbs_in: &[BBox], xforms: &[Matrix4x4], bbs_out: &mut Vec<BBox>) {
|
|
bbs_out.clear();
|
|
|
|
// Transform the bounding boxes
|
|
if xforms.is_empty() {
|
|
bbs_out.extend_from_slice(bbs_in);
|
|
} else if bbs_in.len() == xforms.len() {
|
|
for (bb, xf) in Iterator::zip(bbs_in.iter(), xforms.iter()) {
|
|
bbs_out.push(bb.transformed(xf.inverse()));
|
|
}
|
|
} else if bbs_in.len() > xforms.len() {
|
|
let s = (bbs_in.len() - 1) as f32;
|
|
for (i, bb) in bbs_in.iter().enumerate() {
|
|
bbs_out.push(bb.transformed(lerp_slice(xforms, i as f32 / s).inverse()));
|
|
}
|
|
} else if bbs_in.len() < xforms.len() {
|
|
let s = (xforms.len() - 1) as f32;
|
|
for (i, xf) in xforms.iter().enumerate() {
|
|
bbs_out.push(lerp_slice(bbs_in, i as f32 / s).transformed(xf.inverse()));
|
|
}
|
|
}
|
|
}
|