use crate::{Mat4, Vec3};
use super::Plane;
#[derive(Debug, Clone, Copy)]
pub struct Frustum {
pub planes: [Plane; 6],
}
impl Frustum {
#[inline]
pub fn from_projection(projection: Mat4) -> Self {
let m = projection.as_ref();
let left = Plane::from_normal_d(
Vec3::new(m[3] + m[0], m[7] + m[4], m[11] + m[8]).normalize(),
m[15] + m[12],
).normalize();
let right = Plane::from_normal_d(
Vec3::new(m[3] - m[0], m[7] - m[4], m[11] - m[8]).normalize(),
m[15] - m[12],
).normalize();
let bottom = Plane::from_normal_d(
Vec3::new(m[3] + m[1], m[7] + m[5], m[11] + m[9]).normalize(),
m[15] + m[13],
).normalize();
let top = Plane::from_normal_d(
Vec3::new(m[3] - m[1], m[7] - m[5], m[11] - m[9]).normalize(),
m[15] - m[13],
).normalize();
let near = Plane::from_normal_d(
Vec3::new(m[3] + m[2], m[7] + m[6], m[11] + m[10]).normalize(),
m[15] + m[14],
).normalize();
let far = Plane::from_normal_d(
Vec3::new(m[3] - m[2], m[7] - m[6], m[11] - m[10]).normalize(),
m[15] - m[14],
).normalize();
Self {
planes: [left, right, bottom, top, near, far],
}
}
#[inline]
pub fn contains_point(&self, point: Vec3) -> bool {
self.planes.iter().all(|plane| plane.distance_to_point(point) >= 0.0)
}
#[inline]
pub fn contains_sphere(&self, center: Vec3, radius: f32) -> bool {
self.planes.iter().all(|plane| plane.distance_to_point(center) >= -radius)
}
#[inline]
pub fn contains_aabb(&self, aabb: &super::Aabb3) -> bool {
for plane in &self.planes {
let corners = aabb.corners();
let mut all_behind = true;
for corner in &corners {
if plane.distance_to_point(*corner) >= 0.0 {
all_behind = false;
break;
}
}
if all_behind {
return false;
}
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::aabb::Aabb3;
#[test]
fn test_frustum_creation() {
let proj = Mat4::perspective_rh(std::f32::consts::FRAC_PI_4, 1.0, 0.1, 100.0);
let frustum = Frustum::from_projection(proj);
assert_eq!(frustum.planes.len(), 6);
}
#[test]
fn test_frustum_contains_aabb() {
let proj = Mat4::orthographic_rh(-1.0, 1.0, -1.0, 1.0, 0.1, 100.0);
let frustum = Frustum::from_projection(proj);
let aabb = Aabb3::new(Vec3::new(-0.5, -0.5, -1.0), Vec3::new(0.5, 0.5, -0.5));
let result = frustum.contains_aabb(&aabb);
assert!(result);
}
}