Rename Plane struct to HalfSpace (#8744)

# Objective

- Rename the `render::primitives::Plane` struct as to not confuse it
with `bevy_render::mesh::shape::Plane`
- Fixes https://github.com/bevyengine/bevy/issues/8730

## Solution

- Refactor the `render::primitives::Plane` struct to
`render::primitives::HalfSpace`
- Modify documentation to reflect this change

## Changelog

- Renamed `Plane` to `HalfSpace` to more accurately represent it's use
- Renamed `planes` member in `Frustum` to `half_spaces` to reflect
changes

## Migration Guide

- `Plane` has been renamed to `HalfSpace`
- `planes` member in `Frustum` has been renamed to `half_spaces`

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
This commit is contained in:
lelo 2023-06-12 15:27:41 -04:00 committed by GitHub
parent a78c4d78d5
commit 278daab6ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 64 deletions

View file

@ -8,7 +8,7 @@ use bevy_render::{
color::Color,
extract_resource::ExtractResource,
prelude::Projection,
primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, Plane, Sphere},
primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, HalfSpace, Sphere},
render_resource::BufferBindingType,
renderer::RenderDevice,
view::{ComputedVisibility, RenderLayers, VisibleEntities},
@ -1457,7 +1457,7 @@ pub(crate) fn assign_lights_to_clusters(
let view_x = clip_to_view(inverse_projection, Vec4::new(x_pos, 0.0, 1.0, 1.0)).x;
let normal = Vec3::X;
let d = view_x * normal.x;
x_planes.push(Plane::new(normal.extend(d)));
x_planes.push(HalfSpace::new(normal.extend(d)));
}
let y_slices = clusters.dimensions.y as f32;
@ -1467,7 +1467,7 @@ pub(crate) fn assign_lights_to_clusters(
let view_y = clip_to_view(inverse_projection, Vec4::new(0.0, y_pos, 1.0, 1.0)).y;
let normal = Vec3::Y;
let d = view_y * normal.y;
y_planes.push(Plane::new(normal.extend(d)));
y_planes.push(HalfSpace::new(normal.extend(d)));
}
} else {
let x_slices = clusters.dimensions.x as f32;
@ -1478,7 +1478,7 @@ pub(crate) fn assign_lights_to_clusters(
let nt = clip_to_view(inverse_projection, Vec4::new(x_pos, 1.0, 1.0, 1.0)).xyz();
let normal = nb.cross(nt);
let d = nb.dot(normal);
x_planes.push(Plane::new(normal.extend(d)));
x_planes.push(HalfSpace::new(normal.extend(d)));
}
let y_slices = clusters.dimensions.y as f32;
@ -1489,7 +1489,7 @@ pub(crate) fn assign_lights_to_clusters(
let nr = clip_to_view(inverse_projection, Vec4::new(1.0, y_pos, 1.0, 1.0)).xyz();
let normal = nr.cross(nl);
let d = nr.dot(normal);
y_planes.push(Plane::new(normal.extend(d)));
y_planes.push(HalfSpace::new(normal.extend(d)));
}
}
@ -1498,7 +1498,7 @@ pub(crate) fn assign_lights_to_clusters(
let view_z = z_slice_to_view_z(first_slice_depth, far_z, z_slices, z, is_orthographic);
let normal = -Vec3::Z;
let d = view_z * normal.z;
z_planes.push(Plane::new(normal.extend(d)));
z_planes.push(HalfSpace::new(normal.extend(d)));
}
let mut update_from_light_intersections = |visible_lights: &mut Vec<Entity>| {
@ -1737,7 +1737,7 @@ pub(crate) fn assign_lights_to_clusters(
}
// NOTE: This exploits the fact that a x-plane normal has only x and z components
fn get_distance_x(plane: Plane, point: Vec3A, is_orthographic: bool) -> f32 {
fn get_distance_x(plane: HalfSpace, point: Vec3A, is_orthographic: bool) -> f32 {
if is_orthographic {
point.x - plane.d()
} else {
@ -1750,7 +1750,7 @@ fn get_distance_x(plane: Plane, point: Vec3A, is_orthographic: bool) -> f32 {
}
// NOTE: This exploits the fact that a z-plane normal has only a z component
fn project_to_plane_z(z_light: Sphere, z_plane: Plane) -> Option<Sphere> {
fn project_to_plane_z(z_light: Sphere, z_plane: HalfSpace) -> Option<Sphere> {
// p = sphere center
// n = plane normal
// d = n.p if p is in the plane
@ -1772,7 +1772,11 @@ fn project_to_plane_z(z_light: Sphere, z_plane: Plane) -> Option<Sphere> {
}
// NOTE: This exploits the fact that a y-plane normal has only y and z components
fn project_to_plane_y(y_light: Sphere, y_plane: Plane, is_orthographic: bool) -> Option<Sphere> {
fn project_to_plane_y(
y_light: Sphere,
y_plane: HalfSpace,
is_orthographic: bool,
) -> Option<Sphere> {
let distance_to_plane = if is_orthographic {
y_plane.d() - y_light.center.y
} else {

View file

@ -81,21 +81,21 @@ impl Sphere {
}
}
/// A plane defined by a unit normal and distance from the origin along the normal
/// Any point `p` is in the plane if `n.p + d = 0`
/// For planes defining half-spaces such as for frusta, if `n.p + d > 0` then `p` is on
/// the positive side (inside) of the plane.
/// A bisecting plane that partitions 3D space into two regions.
///
/// Each instance of this type is characterized by the bisecting plane's unit normal and distance from the origin along the normal.
/// Any point `p` is considered to be within the `HalfSpace` when the distance is positive,
/// meaning: if the equation `n.p + d > 0` is satisfied.
#[derive(Clone, Copy, Debug, Default)]
pub struct Plane {
pub struct HalfSpace {
normal_d: Vec4,
}
impl Plane {
/// Constructs a `Plane` from a 4D vector whose first 3 components
/// are the normal and whose last component is the distance along the normal
/// from the origin.
/// This constructor ensures that the normal is normalized and the distance is
/// scaled accordingly so it represents the signed distance from the origin.
impl HalfSpace {
/// Constructs a `HalfSpace` from a 4D vector whose first 3 components
/// represent the bisecting plane's unit normal, and the last component signifies
/// the distance from the origin to the plane along the normal.
/// The constructor ensures the normal vector is normalized and the distance is appropriately scaled.
#[inline]
pub fn new(normal_d: Vec4) -> Self {
Self {
@ -103,35 +103,34 @@ impl Plane {
}
}
/// `Plane` unit normal
/// Returns the unit normal vector of the bisecting plane that characterizes the `HalfSpace`.
#[inline]
pub fn normal(&self) -> Vec3A {
Vec3A::from(self.normal_d)
}
/// Signed distance from the origin along the unit normal such that n.p + d = 0 for point p in
/// the `Plane`
/// Returns the distance from the origin to the bisecting plane along the plane's unit normal vector.
/// This distance helps determine the position of a point `p` on the bisecting plane, as per the equation `n.p + d = 0`.
#[inline]
pub fn d(&self) -> f32 {
self.normal_d.w
}
/// `Plane` unit normal and signed distance from the origin such that n.p + d = 0 for point p
/// in the `Plane`
/// Returns the bisecting plane's unit normal vector and the distance from the origin to the plane.
#[inline]
pub fn normal_d(&self) -> Vec4 {
self.normal_d
}
}
/// A frustum defined by the 6 containing planes
/// Planes are ordered left, right, top, bottom, near, far
/// Normals point into the contained volume
/// A frustum made up of the 6 defining half spaces.
/// Half spaces are ordered left, right, top, bottom, near, far.
/// The normal vectors of the half spaces point towards the interior of the frustum.
#[derive(Component, Clone, Copy, Debug, Default, Reflect)]
#[reflect(Component)]
pub struct Frustum {
#[reflect(ignore)]
pub planes: [Plane; 6],
pub half_spaces: [HalfSpace; 6],
}
impl Frustum {
@ -139,12 +138,12 @@ impl Frustum {
#[inline]
pub fn from_view_projection(view_projection: &Mat4) -> Self {
let mut frustum = Frustum::from_view_projection_no_far(view_projection);
frustum.planes[5] = Plane::new(view_projection.row(2));
frustum.half_spaces[5] = HalfSpace::new(view_projection.row(2));
frustum
}
/// Returns a frustum derived from `view_projection`, but with a custom
/// far plane.
/// Returns a frustum derived from `view_projection`,
/// but with a custom far plane.
#[inline]
pub fn from_view_projection_custom_far(
view_projection: &Mat4,
@ -154,39 +153,44 @@ impl Frustum {
) -> Self {
let mut frustum = Frustum::from_view_projection_no_far(view_projection);
let far_center = *view_translation - far * *view_backward;
frustum.planes[5] = Plane::new(view_backward.extend(-view_backward.dot(far_center)));
frustum.half_spaces[5] =
HalfSpace::new(view_backward.extend(-view_backward.dot(far_center)));
frustum
}
// NOTE: This approach of extracting the frustum planes from the view
// NOTE: This approach of extracting the frustum half-space from the view
// projection matrix is from Foundations of Game Engine Development 2
// Rendering by Lengyel.
/// Returns a frustum derived from `view_projection`,
/// without a far plane.
fn from_view_projection_no_far(view_projection: &Mat4) -> Self {
let row3 = view_projection.row(3);
let mut planes = [Plane::default(); 6];
for (i, plane) in planes.iter_mut().enumerate().take(5) {
let mut half_spaces = [HalfSpace::default(); 6];
for (i, half_space) in half_spaces.iter_mut().enumerate().take(5) {
let row = view_projection.row(i / 2);
*plane = Plane::new(if (i & 1) == 0 && i != 4 {
*half_space = HalfSpace::new(if (i & 1) == 0 && i != 4 {
row3 + row
} else {
row3 - row
});
}
Self { planes }
Self { half_spaces }
}
/// Checks if a sphere intersects the frustum.
#[inline]
pub fn intersects_sphere(&self, sphere: &Sphere, intersect_far: bool) -> bool {
let sphere_center = sphere.center.extend(1.0);
let max = if intersect_far { 6 } else { 5 };
for plane in &self.planes[..max] {
if plane.normal_d().dot(sphere_center) + sphere.radius <= 0.0 {
for half_space in &self.half_spaces[..max] {
if half_space.normal_d().dot(sphere_center) + sphere.radius <= 0.0 {
return false;
}
}
true
}
/// Checks if an Oriented Bounding Box (obb) intersects the frustum.
#[inline]
pub fn intersects_obb(
&self,
@ -202,16 +206,16 @@ impl Frustum {
Vec3A::from(model_to_world.z_axis),
];
for (idx, plane) in self.planes.into_iter().enumerate() {
for (idx, half_space) in self.half_spaces.into_iter().enumerate() {
if idx == 4 && !intersect_near {
continue;
}
if idx == 5 && !intersect_far {
continue;
}
let p_normal = plane.normal();
let p_normal = half_space.normal();
let relative_radius = aabb.relative_radius(&p_normal, &axes);
if plane.normal_d().dot(aabb_center_world) + relative_radius <= 0.0 {
if half_space.normal_d().dot(aabb_center_world) + relative_radius <= 0.0 {
return false;
}
}
@ -249,13 +253,13 @@ mod tests {
// A big, offset frustum
fn big_frustum() -> Frustum {
Frustum {
planes: [
Plane::new(Vec4::new(-0.9701, -0.2425, -0.0000, 7.7611)),
Plane::new(Vec4::new(-0.0000, 1.0000, -0.0000, 4.0000)),
Plane::new(Vec4::new(-0.0000, -0.2425, -0.9701, 2.9104)),
Plane::new(Vec4::new(-0.0000, -1.0000, -0.0000, 4.0000)),
Plane::new(Vec4::new(-0.0000, -0.2425, 0.9701, 2.9104)),
Plane::new(Vec4::new(0.9701, -0.2425, -0.0000, -1.9403)),
half_spaces: [
HalfSpace::new(Vec4::new(-0.9701, -0.2425, -0.0000, 7.7611)),
HalfSpace::new(Vec4::new(-0.0000, 1.0000, -0.0000, 4.0000)),
HalfSpace::new(Vec4::new(-0.0000, -0.2425, -0.9701, 2.9104)),
HalfSpace::new(Vec4::new(-0.0000, -1.0000, -0.0000, 4.0000)),
HalfSpace::new(Vec4::new(-0.0000, -0.2425, 0.9701, 2.9104)),
HalfSpace::new(Vec4::new(0.9701, -0.2425, -0.0000, -1.9403)),
],
}
}
@ -285,13 +289,13 @@ mod tests {
// A frustum
fn frustum() -> Frustum {
Frustum {
planes: [
Plane::new(Vec4::new(-0.9701, -0.2425, -0.0000, 0.7276)),
Plane::new(Vec4::new(-0.0000, 1.0000, -0.0000, 1.0000)),
Plane::new(Vec4::new(-0.0000, -0.2425, -0.9701, 0.7276)),
Plane::new(Vec4::new(-0.0000, -1.0000, -0.0000, 1.0000)),
Plane::new(Vec4::new(-0.0000, -0.2425, 0.9701, 0.7276)),
Plane::new(Vec4::new(0.9701, -0.2425, -0.0000, 0.7276)),
half_spaces: [
HalfSpace::new(Vec4::new(-0.9701, -0.2425, -0.0000, 0.7276)),
HalfSpace::new(Vec4::new(-0.0000, 1.0000, -0.0000, 1.0000)),
HalfSpace::new(Vec4::new(-0.0000, -0.2425, -0.9701, 0.7276)),
HalfSpace::new(Vec4::new(-0.0000, -1.0000, -0.0000, 1.0000)),
HalfSpace::new(Vec4::new(-0.0000, -0.2425, 0.9701, 0.7276)),
HalfSpace::new(Vec4::new(0.9701, -0.2425, -0.0000, 0.7276)),
],
}
}
@ -365,13 +369,13 @@ mod tests {
// A long frustum.
fn long_frustum() -> Frustum {
Frustum {
planes: [
Plane::new(Vec4::new(-0.9998, -0.0222, -0.0000, -1.9543)),
Plane::new(Vec4::new(-0.0000, 1.0000, -0.0000, 45.1249)),
Plane::new(Vec4::new(-0.0000, -0.0168, -0.9999, 2.2718)),
Plane::new(Vec4::new(-0.0000, -1.0000, -0.0000, 45.1249)),
Plane::new(Vec4::new(-0.0000, -0.0168, 0.9999, 2.2718)),
Plane::new(Vec4::new(0.9998, -0.0222, -0.0000, 7.9528)),
half_spaces: [
HalfSpace::new(Vec4::new(-0.9998, -0.0222, -0.0000, -1.9543)),
HalfSpace::new(Vec4::new(-0.0000, 1.0000, -0.0000, 45.1249)),
HalfSpace::new(Vec4::new(-0.0000, -0.0168, -0.9999, 2.2718)),
HalfSpace::new(Vec4::new(-0.0000, -1.0000, -0.0000, 45.1249)),
HalfSpace::new(Vec4::new(-0.0000, -0.0168, 0.9999, 2.2718)),
HalfSpace::new(Vec4::new(0.9998, -0.0222, -0.0000, 7.9528)),
],
}
}