use crate::geometry::Box3D;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum ClipSpaceZ {
#[default]
ZeroToOne,
NegOneToOne,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Frustum3D {
planes: [[f64; 4]; 6],
}
impl Frustum3D {
#[inline]
pub const fn from_planes(planes: [[f64; 4]; 6]) -> Self {
Self { planes }
}
pub fn from_view_projection(vp: [[f64; 4]; 4], clip: ClipSpaceZ) -> Self {
let row = |i: usize| vp[i];
let add = |a: [f64; 4], b: [f64; 4]| [a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]];
let sub = |a: [f64; 4], b: [f64; 4]| [a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]];
let (r0, r1, r2, r3) = (row(0), row(1), row(2), row(3));
let near = match clip {
ClipSpaceZ::ZeroToOne => r2, ClipSpaceZ::NegOneToOne => add(r3, r2), };
Self {
planes: [
add(r3, r0), sub(r3, r0), add(r3, r1), sub(r3, r1), near, sub(r3, r2), ],
}
}
#[inline]
pub fn planes(&self) -> &[[f64; 4]; 6] {
&self.planes
}
#[inline]
pub fn overlaps_box(&self, b: Box3D) -> bool {
for p in &self.planes {
let px = if p[0] >= 0.0 { b.max_x } else { b.min_x };
let py = if p[1] >= 0.0 { b.max_y } else { b.min_y };
let pz = if p[2] >= 0.0 { b.max_z } else { b.min_z };
if p[0] * px + p[1] * py + p[2] * pz + p[3] < 0.0 {
return false;
}
}
true
}
#[inline]
pub fn contains_box(&self, b: Box3D) -> bool {
for p in &self.planes {
let nx = if p[0] >= 0.0 { b.min_x } else { b.max_x };
let ny = if p[1] >= 0.0 { b.min_y } else { b.max_y };
let nz = if p[2] >= 0.0 { b.min_z } else { b.max_z };
if p[0] * nx + p[1] * ny + p[2] * nz + p[3] < 0.0 {
return false;
}
}
true
}
}