use crate::{Float, Vec3, Vec4, AABB};
use core::fmt::Debug;
pub struct Frustum<T: Float + Debug> {
pub planes: [Vec4<T>; 6],
pub points: [Vec3<T>; 8],
}
impl<T: Float + Debug> Frustum<T> {
pub fn from_modelview_projection(mvp: [[T; 4]; 4]) -> Frustum<T> {
let left = normalize_plane(Vec4::new(
mvp[0][0] + mvp[0][3],
mvp[1][0] + mvp[1][3],
mvp[2][0] + mvp[2][3],
mvp[3][0] + mvp[3][3],
));
let right = normalize_plane(Vec4::new(
-mvp[0][0] + mvp[0][3],
-mvp[1][0] + mvp[1][3],
-mvp[2][0] + mvp[2][3],
-mvp[3][0] + mvp[3][3],
));
let bottom = normalize_plane(Vec4::new(
mvp[0][1] + mvp[0][3],
mvp[1][1] + mvp[1][3],
mvp[2][1] + mvp[2][3],
mvp[3][1] + mvp[3][3],
));
let top = normalize_plane(Vec4::new(
-mvp[0][1] + mvp[0][3],
-mvp[1][1] + mvp[1][3],
-mvp[2][1] + mvp[2][3],
-mvp[3][1] + mvp[3][3],
));
let near = normalize_plane(Vec4::new(
mvp[0][2] + mvp[0][3],
mvp[1][2] + mvp[1][3],
mvp[2][2] + mvp[2][3],
mvp[3][2] + mvp[3][3],
));
let far = normalize_plane(Vec4::new(
-mvp[0][2] + mvp[0][3],
-mvp[1][2] + mvp[1][3],
-mvp[2][2] + mvp[2][3],
-mvp[3][2] + mvp[3][3],
));
let flt = intersect_planes(&far, &left, &top);
let frt = intersect_planes(&far, &right, &top);
let flb = intersect_planes(&far, &left, &bottom);
let frb = intersect_planes(&far, &right, &bottom);
let nlt = intersect_planes(&near, &left, &top);
let nrt = intersect_planes(&near, &right, &top);
let nlb = intersect_planes(&near, &left, &bottom);
let nrb = intersect_planes(&near, &right, &bottom);
Self {
planes: [near, left, right, bottom, top, far],
points: [nlt, nrt, nlb, nrb, flt, frt, flb, frb],
}
}
pub(crate) fn test_against_aabb(&self, aabb: &AABB<T>) -> bool {
for i in 0..3 {
let mut out = 0;
for j in 0..8 {
if self.points[j][i] < aabb.min[i] {
out += 1;
}
}
if out == 8 {
return false;
}
out = 0;
for j in 0..8 {
if self.points[j][i] > aabb.max[i] {
out += 1;
}
}
if out == 8 {
return false;
}
}
true
}
}
pub fn normalize_plane<T: Float>(mut plane: Vec4<T>) -> Vec4<T> {
let normal_magnitude = (plane.x.powi(2) + plane.y.powi(2) + plane.z.powi(2)).sqrt();
plane.x = plane.x / normal_magnitude;
plane.y = plane.y / normal_magnitude;
plane.z = plane.z / normal_magnitude;
plane.w = plane.w / normal_magnitude;
plane
}
pub fn intersect_planes<T: Float>(p0: &Vec4<T>, p1: &Vec4<T>, p2: &Vec4<T>) -> Vec3<T> {
use core::ops::Mul;
let bxc = p1.xyz().cross(p2.xyz());
let cxa = p2.xyz().cross(p0.xyz());
let axb = p0.xyz().cross(p1.xyz());
let r = -bxc.mul(p0.w) - cxa.mul(p1.w) - axb.mul(p2.w);
r * (T::one() / bxc.dot(p0.xyz()))
}