#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Plane {
pub normal: [f32; 3],
pub d: f32,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Frustum {
pub planes: [Plane; 6],
}
fn plane_distance(p: &Plane, pt: [f32; 3]) -> f32 {
p.normal[0] * pt[0] + p.normal[1] * pt[1] + p.normal[2] * pt[2] + p.d
}
fn extract_plane(row0: [f32; 4], row1: [f32; 4], sign: f32) -> Plane {
let nx = row0[3] + sign * row0[0];
let ny = row0[3] + sign * row0[1];
let nz = row0[3] + sign * row0[2];
let d = row1[3] + sign * row1[3];
let len = (nx * nx + ny * ny + nz * nz).sqrt().max(1e-10);
Plane {
normal: [nx / len, ny / len, nz / len],
d: d / len,
}
}
#[allow(dead_code)]
pub fn frustum_from_mat4(m: [[f32; 4]; 4]) -> Frustum {
let make = |a: usize, s: f32| {
let nx = m[3][0] + s * m[a][0];
let ny = m[3][1] + s * m[a][1];
let nz = m[3][2] + s * m[a][2];
let d = m[3][3] + s * m[a][3];
let len = (nx * nx + ny * ny + nz * nz).sqrt().max(1e-10);
Plane {
normal: [nx / len, ny / len, nz / len],
d: d / len,
}
};
let _ = extract_plane([0.0; 4], [0.0; 4], 1.0);
Frustum {
planes: [
make(0, 1.0), make(0, -1.0), make(1, 1.0), make(1, -1.0), make(2, 1.0), make(2, -1.0), ],
}
}
#[allow(dead_code)]
pub fn sphere_in_frustum(f: &Frustum, center: [f32; 3], radius: f32) -> bool {
for plane in &f.planes {
if plane_distance(plane, center) < -radius {
return false;
}
}
true
}
#[allow(dead_code)]
pub fn aabb_in_frustum(f: &Frustum, min: [f32; 3], max: [f32; 3]) -> bool {
for plane in &f.planes {
let p_vertex = [
if plane.normal[0] >= 0.0 { max[0] } else { min[0] },
if plane.normal[1] >= 0.0 { max[1] } else { min[1] },
if plane.normal[2] >= 0.0 { max[2] } else { min[2] },
];
if plane_distance(plane, p_vertex) < 0.0 {
return false;
}
}
true
}
#[allow(dead_code)]
pub fn point_in_frustum(f: &Frustum, pt: [f32; 3]) -> bool {
for plane in &f.planes {
if plane_distance(plane, pt) < 0.0 {
return false;
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
fn make_identity_frustum() -> Frustum {
let make_plane = |nx: f32, ny: f32, nz: f32, d: f32| Plane {
normal: [nx, ny, nz],
d,
};
Frustum {
planes: [
make_plane(1.0, 0.0, 0.0, 1.0), make_plane(-1.0, 0.0, 0.0, 1.0), make_plane(0.0, 1.0, 0.0, 1.0), make_plane(0.0, -1.0, 0.0, 1.0), make_plane(0.0, 0.0, 1.0, 1.0), make_plane(0.0, 0.0, -1.0, 1.0), ],
}
}
#[test]
fn test_point_inside() {
let f = make_identity_frustum();
assert!(point_in_frustum(&f, [0.0, 0.0, 0.0]));
}
#[test]
fn test_point_outside() {
let f = make_identity_frustum();
assert!(!point_in_frustum(&f, [2.0, 0.0, 0.0]));
}
#[test]
fn test_sphere_inside() {
let f = make_identity_frustum();
assert!(sphere_in_frustum(&f, [0.0, 0.0, 0.0], 0.5));
}
#[test]
fn test_sphere_outside() {
let f = make_identity_frustum();
assert!(!sphere_in_frustum(&f, [5.0, 0.0, 0.0], 0.1));
}
#[test]
fn test_sphere_intersecting() {
let f = make_identity_frustum();
assert!(sphere_in_frustum(&f, [1.5, 0.0, 0.0], 1.0));
}
#[test]
fn test_aabb_inside() {
let f = make_identity_frustum();
assert!(aabb_in_frustum(&f, [-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]));
}
#[test]
fn test_aabb_outside() {
let f = make_identity_frustum();
assert!(!aabb_in_frustum(&f, [2.0, 2.0, 2.0], [3.0, 3.0, 3.0]));
}
#[test]
fn test_plane_distance_positive() {
let p = Plane { normal: [1.0, 0.0, 0.0], d: 0.0 };
assert!(plane_distance(&p, [1.0, 0.0, 0.0]) > 0.0);
}
#[test]
fn test_plane_distance_negative() {
let p = Plane { normal: [1.0, 0.0, 0.0], d: 0.0 };
assert!(plane_distance(&p, [-1.0, 0.0, 0.0]) < 0.0);
}
}