Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Planes and Culling Frustums #1337

Open
ggadwa opened this issue Dec 22, 2023 · 1 comment
Open

Planes and Culling Frustums #1337

ggadwa opened this issue Dec 22, 2023 · 1 comment

Comments

@ggadwa
Copy link

ggadwa commented Dec 22, 2023

Any thoughts about adding in planes and culling frustums (or just frustums with the right functions.) I'm using nalgebra for a game I'm working on and planes + frustums are nice for optimizations, so others might desire them.

I basically made my own and if you think this is something useful, feel free to use my code if it's up to your standards, at least as a start. Otherwise, it's not hard to create, just something that could get wrapped into your lib and be one less piece of code I'm maintaining.

If you already have this and I somehow missed it, I apologize for the issue!

Plane:

`
use num_traits::real::Real;
use nalgebra::*;

pub struct Plane<T: Real> {
pub a: T,
pub b: T,
pub c: T,
pub d: T
}

impl<T: Real + std::fmt::Debug + 'static> Plane {
pub fn new(
a: T,
b: T,
c: T,
d: T
) -> Self {
Self {
a,
b,
c,
d
}
}

pub fn normalize(
    &mut self
) {
    let f = ((self.a * self.a) + (self.b * self.b) + (self.c * self.c)).sqrt();
    if f != num_traits::zero() {
        self.a = self.a / f;
        self.b = self.b / f;
        self.c = self.c / f;
        self.d = self.d / f;
    }
}

pub fn bound_box_outside_plane(
    &mut self,
    min: &Point3<T>,
    max: &Point3<T>
) -> bool {
    if (self.a * min.x) + (self.b * min.y) + (self.c * min.z) + self.d > num_traits::zero() {
        return true;
    }
	if (self.a * max.x) + (self.b * min.y) + (self.c * min.z) + self.d > num_traits::zero() {
        return true;
    }
	if (self.a * min.x) + (self.b * max.y) + (self.c * min.z) + self.d > num_traits::zero() {
        return true;
    }
	if (self.a * max.x) + (self.b * max.y) + (self.c * min.z) + self.d > num_traits::zero() {
        return true;
    }
	if (self.a * min.x) + (self.b * min.y) + (self.c * max.z) + self.d > num_traits::zero() {
        return true;
    }
	if (self.a * max.x) + (self.b * min.y) + (self.c * max.z) + self.d > num_traits::zero() {
        return true;
    }
	if (self.a * min.x) + (self.b * max.y) + (self.c * max.z) + self.d > num_traits::zero() {
        return true;
    }
	(self.a * max.x) + (self.b * max.y) + (self.c * max.z) + self.d > num_traits::zero()
}

}

impl<T: Real + std::fmt::Debug + 'static> Default for Plane {
fn default() -> Self {
Self {
a: num_traits::zero(),
b: num_traits::zero(),
c: num_traits::zero(),
d: num_traits::zero()
}
}
}
`

Culling Frustum:

`
use num_traits::real::Real;
use nalgebra::*;

use crate::game::view_plane::Plane;
pub struct CullingFrustum<T: Real> {
pub left: Plane,
pub right: Plane,
pub top: Plane,
pub bottom: Plane,
pub near: Plane,
pub far: Plane,
}

impl<T: Real + std::fmt::Debug + 'static> CullingFrustum {
pub fn new(
perspective_matrix: &Matrix4,
view_matrix: &Matrix4
) -> Self {
let mut clip_plane: [T; 16] = [num_traits::zero(); 16];

    clip_plane[0]=(view_matrix[(0,0)]*perspective_matrix[(0,0)])+(view_matrix[(1,0)]*perspective_matrix[(0,1)])+(view_matrix[(2,0)]*perspective_matrix[(0,2)])+(view_matrix[(3,0)]*perspective_matrix[(0,3)]);
    clip_plane[1]=(view_matrix[(0,0)]*perspective_matrix[(1,0)])+(view_matrix[(1,0)]*perspective_matrix[(1,1)])+(view_matrix[(2,0)]*perspective_matrix[(1,2)])+(view_matrix[(3,0)]*perspective_matrix[(1,3)]);
    clip_plane[2]=(view_matrix[(0,0)]*perspective_matrix[(2,0)])+(view_matrix[(1,0)]*perspective_matrix[(2,1)])+(view_matrix[(2,0)]*perspective_matrix[(2,2)])+(view_matrix[(3,0)]*perspective_matrix[(2,3)]);
    clip_plane[3]=(view_matrix[(0,0)]*perspective_matrix[(3,0)])+(view_matrix[(1,0)]*perspective_matrix[(3,1)])+(view_matrix[(2,0)]*perspective_matrix[(3,2)])+(view_matrix[(3,0)]*perspective_matrix[(3,3)]);

    clip_plane[4]=(view_matrix[(0,1)]*perspective_matrix[(0,0)])+(view_matrix[(1,1)]*perspective_matrix[(0,1)])+(view_matrix[(2,1)]*perspective_matrix[(0,2)])+(view_matrix[(3,1)]*perspective_matrix[(0,3)]);
    clip_plane[5]=(view_matrix[(0,1)]*perspective_matrix[(1,0)])+(view_matrix[(1,1)]*perspective_matrix[(1,1)])+(view_matrix[(2,1)]*perspective_matrix[(1,2)])+(view_matrix[(3,1)]*perspective_matrix[(1,3)]);
    clip_plane[6]=(view_matrix[(0,1)]*perspective_matrix[(2,0)])+(view_matrix[(1,1)]*perspective_matrix[(2,1)])+(view_matrix[(2,1)]*perspective_matrix[(2,2)])+(view_matrix[(3,1)]*perspective_matrix[(2,3)]);
    clip_plane[7]=(view_matrix[(0,1)]*perspective_matrix[(3,0)])+(view_matrix[(1,1)]*perspective_matrix[(3,1)])+(view_matrix[(2,1)]*perspective_matrix[(3,2)])+(view_matrix[(3,1)]*perspective_matrix[(3,3)]);

    clip_plane[8]=(view_matrix[(0,2)]*perspective_matrix[(0,0)])+(view_matrix[(1,2)]*perspective_matrix[(0,1)])+(view_matrix[(2,2)]*perspective_matrix[(0,2)])+(view_matrix[(3,2)]*perspective_matrix[(0,3)]);
    clip_plane[9]=(view_matrix[(0,2)]*perspective_matrix[(1,0)])+(view_matrix[(1,2)]*perspective_matrix[(1,1)])+(view_matrix[(2,2)]*perspective_matrix[(1,2)])+(view_matrix[(3,2)]*perspective_matrix[(1,3)]);
    clip_plane[10]=(view_matrix[(0,2)]*perspective_matrix[(2,0)])+(view_matrix[(1,2)]*perspective_matrix[(2,1)])+(view_matrix[(2,2)]*perspective_matrix[(2,2)])+(view_matrix[(3,2)]*perspective_matrix[(2,3)]);
    clip_plane[11]=(view_matrix[(0,2)]*perspective_matrix[(3,0)])+(view_matrix[(1,2)]*perspective_matrix[(3,1)])+(view_matrix[(2,2)]*perspective_matrix[(3,2)])+(view_matrix[(3,2)]*perspective_matrix[(3,3)]);

    clip_plane[12]=(view_matrix[(0,3)]*perspective_matrix[(0,0)])+(view_matrix[(1,3)]*perspective_matrix[(0,1)])+(view_matrix[(2,3)]*perspective_matrix[(0,2)])+(view_matrix[(3,3)]*perspective_matrix[(0,3)]);
    clip_plane[13]=(view_matrix[(0,3)]*perspective_matrix[(1,0)])+(view_matrix[(1,3)]*perspective_matrix[(1,1)])+(view_matrix[(2,3)]*perspective_matrix[(1,2)])+(view_matrix[(3,3)]*perspective_matrix[(1,3)]);
    clip_plane[14]=(view_matrix[(0,3)]*perspective_matrix[(2,0)])+(view_matrix[(1,3)]*perspective_matrix[(2,1)])+(view_matrix[(2,3)]*perspective_matrix[(2,2)])+(view_matrix[(3,3)]*perspective_matrix[(2,3)]);
    clip_plane[15]=(view_matrix[(0,3)]*perspective_matrix[(3,0)])+(view_matrix[(1,3)]*perspective_matrix[(3,1)])+(view_matrix[(2,3)]*perspective_matrix[(3,2)])+(view_matrix[(3,3)]*perspective_matrix[(3,3)]);

    // left plane
    let mut left: Plane<T> = Plane::new(
        clip_plane[3]+clip_plane[0],
        clip_plane[7]+clip_plane[4],
        clip_plane[11]+clip_plane[8],
        clip_plane[15]+clip_plane[12]
    );
    left.normalize();
    
    // right plane
    let mut right: Plane<T> = Plane::new(
        clip_plane[3]-clip_plane[0],
        clip_plane[7]-clip_plane[4],
        clip_plane[11]-clip_plane[8],
        clip_plane[15]-clip_plane[12]
    );
    right.normalize();
    
    // top plane
    let mut top: Plane<T> = Plane::new(
        clip_plane[3]-clip_plane[1],
        clip_plane[7]-clip_plane[5],
        clip_plane[11]-clip_plane[9],
        clip_plane[15]-clip_plane[13]
    );
    top.normalize();
    
    // bottom plane
    let mut bottom: Plane<T> = Plane::new(
        clip_plane[3]+clip_plane[1],
        clip_plane[7]+clip_plane[5],
        clip_plane[11]+clip_plane[9],
        clip_plane[15]+clip_plane[13]
    );
    bottom.normalize();
    
    // near plane
    let mut near: Plane<T> = Plane::new(
        clip_plane[3]+clip_plane[2],
        clip_plane[7]+clip_plane[6],
        clip_plane[11]+clip_plane[10],
        clip_plane[15]+clip_plane[14]
    );
    near.normalize();
    
    // far plane
    let mut far: Plane<T> = Plane::new(
        clip_plane[3]-clip_plane[2],
        clip_plane[7]-clip_plane[6],
        clip_plane[11]-clip_plane[10],
        clip_plane[15]-clip_plane[14]
    );
    far.normalize();

    Self {
        left,
        right,
        top,
        bottom,
        near,
        far
    }
}

pub fn bound_box_in_frustum(
    &mut self,
    min: &Point3<T>,
    max: &Point3<T>
) -> bool {
    // check if outside the plane, if it is,
    // then it's considered outside the bounds
    if !self.left.bound_box_outside_plane(min, max) {
        return false;
    }
    if !self.right.bound_box_outside_plane(min, max) {
        return false;
    }
    if !self.top.bound_box_outside_plane(min, max) {
        return false;
    }
    if !self.bottom.bound_box_outside_plane(min, max) {
        return false;
    }
    if !self.near.bound_box_outside_plane(min, max) {
        return false;
    }
    self.far.bound_box_outside_plane(min, max)
}

}

impl<T: Real + std::fmt::Debug + 'static> Default for CullingFrustum {
fn default() -> Self {
Self {
left: Plane::default(),
right: Plane::default(),
top: Plane::default(),
bottom: Plane::default(),
near: Plane::default(),
far: Plane::default(),
}
}
}
`

@tpdickso
Copy link
Collaborator

I think nalgebra's built-in geometric types tend to be constrained to more algebraic types that are related to vectors/matrices -- however, I think the HalfSpace structure from nalgebra's sister library parry could be what you're looking for! It has a focus specifically on collision detection and so could be useful for your use-case. I don't think parry currently has a structure for a 6-plane frustum, though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants