From 278daab6aef38f3254c6a74790ddf2718fbf514a Mon Sep 17 00:00:00 2001 From: lelo <15314665+hate@users.noreply.github.com> Date: Mon, 12 Jun 2023 15:27:41 -0400 Subject: [PATCH] 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 Co-authored-by: Nicola Papale --- crates/bevy_pbr/src/light.rs | 22 +++-- crates/bevy_render/src/primitives/mod.rs | 114 ++++++++++++----------- 2 files changed, 72 insertions(+), 64 deletions(-) diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index 05bab22965..fd2782a6d0 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -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| { @@ -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 { +fn project_to_plane_z(z_light: Sphere, z_plane: HalfSpace) -> Option { // 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 { } // 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 { +fn project_to_plane_y( + y_light: Sphere, + y_plane: HalfSpace, + is_orthographic: bool, +) -> Option { let distance_to_plane = if is_orthographic { y_plane.d() - y_light.center.y } else { diff --git a/crates/bevy_render/src/primitives/mod.rs b/crates/bevy_render/src/primitives/mod.rs index 4bf88cda08..5beee4936d 100644 --- a/crates/bevy_render/src/primitives/mod.rs +++ b/crates/bevy_render/src/primitives/mod.rs @@ -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)), ], } }