Last active
January 28, 2023 20:42
-
-
Save griffi-gh/a6ed5ed3bc7e7ac8e29974502abafb40 to your computer and use it in GitHub Desktop.
Rust Frustum culling code (using glam)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// basically ported from c++ | |
// - used as a reference: | |
// [ https://github.com/Beastwick18/gltest/blob/main/src/renderer/Frustum.cpp ] | |
// - original code: | |
// [ https://gist.github.com/podgorskiy/e698d18879588ada9014768e3e82a644 ] | |
// - which uses cube vs frustum intersection code from: | |
// [ http://iquilezles.org/www/articles/frustumcorrect/frustumcorrect.htm ] | |
// three layers of stolen code, yay! | |
use glam::{Vec3, Vec3A, vec3a, Vec4, vec4, Mat3A, Mat4}; | |
#[repr(usize)] | |
enum FrustumPlane { | |
Left, | |
Right, | |
Bottom, | |
Top, | |
Near, | |
Far, | |
} | |
const PLANE_COUNT: usize = 6; | |
const PLANE_COMBINATIONS: usize = PLANE_COUNT * (PLANE_COUNT - 1) / 2; | |
const POINT_COUNT: usize = 8; | |
#[derive(Default)] | |
pub struct Frustum { | |
planes: [Vec4; PLANE_COUNT], | |
points: [Vec3A; POINT_COUNT] | |
} | |
impl Frustum { | |
pub fn compute(perspective_matrix: Mat4, view_matrix: Mat4) -> Self { | |
//compute transposed view-projection matrix | |
let mat = (perspective_matrix * view_matrix).transpose(); | |
// compute planes | |
let mut planes = [Vec4::default(); PLANE_COUNT]; | |
planes[FrustumPlane::Left as usize] = mat.w_axis + mat.x_axis; | |
planes[FrustumPlane::Right as usize] = mat.w_axis - mat.x_axis; | |
planes[FrustumPlane::Bottom as usize] = mat.w_axis + mat.y_axis; | |
planes[FrustumPlane::Top as usize] = mat.w_axis - mat.y_axis; | |
planes[FrustumPlane::Near as usize] = mat.w_axis + mat.z_axis; | |
planes[FrustumPlane::Far as usize] = mat.w_axis - mat.z_axis; | |
//compute crosses | |
let crosses = [ | |
Vec3A::from(planes[FrustumPlane::Left as usize]).cross(planes[FrustumPlane::Right as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Left as usize]).cross(planes[FrustumPlane::Bottom as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Left as usize]).cross(planes[FrustumPlane::Top as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Left as usize]).cross(planes[FrustumPlane::Near as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Left as usize]).cross(planes[FrustumPlane::Far as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Right as usize]).cross(planes[FrustumPlane::Bottom as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Right as usize]).cross(planes[FrustumPlane::Top as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Right as usize]).cross(planes[FrustumPlane::Near as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Right as usize]).cross(planes[FrustumPlane::Far as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Bottom as usize]).cross(planes[FrustumPlane::Top as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Bottom as usize]).cross(planes[FrustumPlane::Near as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Bottom as usize]).cross(planes[FrustumPlane::Far as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Top as usize]).cross(planes[FrustumPlane::Near as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Top as usize]).cross(planes[FrustumPlane::Far as usize].into()), | |
Vec3A::from(planes[FrustumPlane::Near as usize]).cross(planes[FrustumPlane::Far as usize].into()), | |
]; | |
//compute points | |
let points = [ | |
intersection::<{FrustumPlane::Left as usize}, {FrustumPlane::Bottom as usize}, {FrustumPlane::Near as usize}>(&planes, &crosses), | |
intersection::<{FrustumPlane::Left as usize}, {FrustumPlane::Top as usize}, {FrustumPlane::Near as usize}>(&planes, &crosses), | |
intersection::<{FrustumPlane::Right as usize}, {FrustumPlane::Bottom as usize}, {FrustumPlane::Near as usize}>(&planes, &crosses), | |
intersection::<{FrustumPlane::Right as usize}, {FrustumPlane::Top as usize}, {FrustumPlane::Near as usize}>(&planes, &crosses), | |
intersection::<{FrustumPlane::Left as usize}, {FrustumPlane::Bottom as usize}, {FrustumPlane::Far as usize}>(&planes, &crosses), | |
intersection::<{FrustumPlane::Left as usize}, {FrustumPlane::Top as usize}, {FrustumPlane::Far as usize}>(&planes, &crosses), | |
intersection::<{FrustumPlane::Right as usize}, {FrustumPlane::Bottom as usize}, {FrustumPlane::Far as usize}>(&planes, &crosses), | |
intersection::<{FrustumPlane::Right as usize}, {FrustumPlane::Top as usize}, {FrustumPlane::Far as usize}>(&planes, &crosses), | |
]; | |
Self { planes, points } | |
} | |
pub fn is_box_visible(&self, minp: Vec3, maxp: Vec3) -> bool { | |
// check box outside/inside of frustum | |
for plane in self.planes { | |
if (plane.dot(vec4(minp.x, minp.y, minp.z, 1.)) < 0.) && | |
(plane.dot(vec4(maxp.x, minp.y, minp.z, 1.)) < 0.) && | |
(plane.dot(vec4(minp.x, maxp.y, minp.z, 1.)) < 0.) && | |
(plane.dot(vec4(maxp.x, maxp.y, minp.z, 1.)) < 0.) && | |
(plane.dot(vec4(minp.x, minp.y, maxp.z, 1.)) < 0.) && | |
(plane.dot(vec4(maxp.x, minp.y, maxp.z, 1.)) < 0.) && | |
(plane.dot(vec4(minp.x, maxp.y, maxp.z, 1.)) < 0.) && | |
(plane.dot(vec4(maxp.x, maxp.y, maxp.z, 1.)) < 0.) | |
{ | |
return false | |
} | |
} | |
// check frustum outside/inside box | |
if self.points.iter().all(|point| point.x > maxp.x) { return false } | |
if self.points.iter().all(|point| point.x < minp.x) { return false } | |
if self.points.iter().all(|point| point.y > maxp.y) { return false } | |
if self.points.iter().all(|point| point.y < minp.y) { return false } | |
if self.points.iter().all(|point| point.z > maxp.z) { return false } | |
if self.points.iter().all(|point| point.z < minp.z) { return false } | |
true | |
} | |
} | |
const fn ij2k<const I: usize, const J: usize>() -> usize { | |
I * (9 - I) / 2 + J - 1 | |
} | |
fn intersection<const A: usize, const B: usize, const C: usize>(planes: &[Vec4; PLANE_COUNT], crosses: &[Vec3A; PLANE_COMBINATIONS]) -> Vec3A { | |
let d = Vec3A::from(planes[A]).dot(crosses[ij2k::<B, C>()]); | |
let res = Mat3A::from_cols( | |
crosses[ij2k::<B, C>()], | |
-crosses[ij2k::<A, C>()], | |
crosses[ij2k::<A, B>()], | |
) * vec3a(planes[A].w, planes[B].w, planes[C].w); | |
res * (-1. / d) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment