use glam::{Mat4, Vec3};
#[derive(Debug, Clone, Copy)]
pub struct Frustum {
pub planes: [[f32; 4]; 6],
}
impl Frustum {
pub fn from_view_projection(vp: &Mat4) -> Self {
let m = vp.to_cols_array_2d();
let left = [
m[0][3] + m[0][0],
m[1][3] + m[1][0],
m[2][3] + m[2][0],
m[3][3] + m[3][0],
];
let right = [
m[0][3] - m[0][0],
m[1][3] - m[1][0],
m[2][3] - m[2][0],
m[3][3] - m[3][0],
];
let bottom = [
m[0][3] + m[0][1],
m[1][3] + m[1][1],
m[2][3] + m[2][1],
m[3][3] + m[3][1],
];
let top = [
m[0][3] - m[0][1],
m[1][3] - m[1][1],
m[2][3] - m[2][1],
m[3][3] - m[3][1],
];
let near = [
m[0][3] + m[0][2],
m[1][3] + m[1][2],
m[2][3] + m[2][2],
m[3][3] + m[3][2],
];
let far = [
m[0][3] - m[0][2],
m[1][3] - m[1][2],
m[2][3] - m[2][2],
m[3][3] - m[3][2],
];
let planes = [left, right, bottom, top, near, far].map(normalize_plane);
Self { planes }
}
pub fn intersects_aabb(&self, center: Vec3, half_extents: Vec3) -> bool {
for plane in self.planes {
let n = Vec3::new(plane[0], plane[1], plane[2]);
let d = plane[3];
let p = Vec3::new(
if n.x >= 0.0 {
center.x + half_extents.x
} else {
center.x - half_extents.x
},
if n.y >= 0.0 {
center.y + half_extents.y
} else {
center.y - half_extents.y
},
if n.z >= 0.0 {
center.z + half_extents.z
} else {
center.z - half_extents.z
},
);
if n.dot(p) + d < 0.0 {
return false;
}
}
true
}
pub fn intersects_sphere(&self, center: Vec3, radius: f32) -> bool {
for plane in self.planes {
let n = Vec3::new(plane[0], plane[1], plane[2]);
let d = plane[3];
if n.dot(center) + d < -radius {
return false;
}
}
true
}
}
fn normalize_plane(p: [f32; 4]) -> [f32; 4] {
let n = Vec3::new(p[0], p[1], p[2]);
let len = n.length();
if len == 0.0 {
return p;
}
[p[0] / len, p[1] / len, p[2] / len, p[3] / len]
}