From f87cc5ccc3112efad2aed215b9d434100c4d2d95 Mon Sep 17 00:00:00 2001 From: Aevyrie Roessler Date: Thu, 11 Mar 2021 10:40:59 -0800 Subject: [PATCH 01/12] Add geometric primitives --- crates/bevy_geometry/Cargo.toml | 10 ++++++++++ crates/bevy_geometry/src/lib.rs | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 crates/bevy_geometry/Cargo.toml create mode 100644 crates/bevy_geometry/src/lib.rs diff --git a/crates/bevy_geometry/Cargo.toml b/crates/bevy_geometry/Cargo.toml new file mode 100644 index 0000000000000..47bff5f999aa9 --- /dev/null +++ b/crates/bevy_geometry/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "bevy_geometry" +version = "0.1.0" +authors = ["Aevyrie Roessler "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bevy_math = { path = "../bevy_math", version = "0.4.0" } diff --git a/crates/bevy_geometry/src/lib.rs b/crates/bevy_geometry/src/lib.rs new file mode 100644 index 0000000000000..4906b2cea2400 --- /dev/null +++ b/crates/bevy_geometry/src/lib.rs @@ -0,0 +1,24 @@ +use bevy_math::{Quat, Vec3}; + +pub trait Primitive3d {} + +pub struct Sphere { + pub origin: Vec3, + pub radius: f32, +} + +pub struct Box { + pub maximums: Vec3, + pub minimums: Vec3, + pub orientation: Quat, +} + +pub struct AxisAlignedBox { + pub maximums: Vec3, + pub minimums: Vec3, +} + +pub struct Plane { + pub point: Vec3, + pub normal: Vec3, +} From 5e1e472afe1ca734199b3b49358a6dea42a20ab0 Mon Sep 17 00:00:00 2001 From: Aevyrie Date: Thu, 11 Mar 2021 10:49:57 -0800 Subject: [PATCH 02/12] Update crates/bevy_geometry/Cargo.toml Co-authored-by: MinerSebas <66798382+MinerSebas@users.noreply.github.com> --- crates/bevy_geometry/Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/bevy_geometry/Cargo.toml b/crates/bevy_geometry/Cargo.toml index 47bff5f999aa9..24cc9f781670c 100644 --- a/crates/bevy_geometry/Cargo.toml +++ b/crates/bevy_geometry/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "bevy_geometry" version = "0.1.0" -authors = ["Aevyrie Roessler "] +authors = [ + "Bevy Contributors ", + "Aevyrie Roessler ", +] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 59170411f0d51893dfa90e13ce02d153d31b3301 Mon Sep 17 00:00:00 2001 From: Aevyrie Roessler Date: Thu, 11 Mar 2021 11:10:59 -0800 Subject: [PATCH 03/12] re-exports --- crates/bevy_geometry/Cargo.toml | 7 ++++--- crates/bevy_internal/Cargo.toml | 7 ++++--- crates/bevy_internal/src/lib.rs | 5 +++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/crates/bevy_geometry/Cargo.toml b/crates/bevy_geometry/Cargo.toml index 24cc9f781670c..62aab1c1d29e1 100644 --- a/crates/bevy_geometry/Cargo.toml +++ b/crates/bevy_geometry/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "bevy_geometry" -version = "0.1.0" +version = "0.4.0" authors = [ - "Bevy Contributors ", - "Aevyrie Roessler ", + "Bevy Contributors ", + "Aevyrie Roessler ", ] edition = "2018" @@ -11,3 +11,4 @@ edition = "2018" [dependencies] bevy_math = { path = "../bevy_math", version = "0.4.0" } +bevy_reflect = { path = "../bevy_reflect", version = "0.4.0" } diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index d4556746d3508..88a329f1ded78 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -15,8 +15,8 @@ categories = ["game-engines", "graphics", "gui", "rendering"] [features] wgpu_trace = ["bevy_wgpu/trace"] -trace = [ "bevy_app/trace", "bevy_ecs/trace" ] -trace_chrome = [ "bevy_log/tracing-chrome" ] +trace = ["bevy_app/trace", "bevy_ecs/trace"] +trace_chrome = ["bevy_log/tracing-chrome"] # Image format support for texture loading (PNG and HDR are enabled by default) hdr = ["bevy_render/hdr"] @@ -49,6 +49,7 @@ bevy_core = { path = "../bevy_core", version = "0.4.0" } bevy_derive = { path = "../bevy_derive", version = "0.4.0" } bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.4.0" } bevy_ecs = { path = "../bevy_ecs", version = "0.4.0" } +bevy_geometry = { path = "../bevy_geometry", version = "0.4.0" } bevy_input = { path = "../bevy_input", version = "0.4.0" } bevy_log = { path = "../bevy_log", version = "0.4.0" } bevy_math = { path = "../bevy_math", version = "0.4.0" } @@ -72,4 +73,4 @@ bevy_winit = { path = "../bevy_winit", optional = true, version = "0.4.0" } bevy_gilrs = { path = "../bevy_gilrs", optional = true, version = "0.4.0" } [target.'cfg(target_os = "android")'.dependencies] -ndk-glue = {version = "0.2", features = ["logger"]} +ndk-glue = { version = "0.2", features = ["logger"] } diff --git a/crates/bevy_internal/src/lib.rs b/crates/bevy_internal/src/lib.rs index 331a5e90ab256..0a1e8ea6e2c44 100644 --- a/crates/bevy_internal/src/lib.rs +++ b/crates/bevy_internal/src/lib.rs @@ -29,6 +29,11 @@ pub mod ecs { pub use bevy_ecs::*; } +pub mod geometry { + //! Geometric primitives + pub use bevy_geometry::*; +} + pub mod input { //! Resources and events for inputs, e.g. mouse/keyboard, touch, gamepads, etc. pub use bevy_input::*; From 557c31a510580c6fc824fbe4fb874666428611dd Mon Sep 17 00:00:00 2001 From: Aevyrie Roessler Date: Thu, 11 Mar 2021 11:11:09 -0800 Subject: [PATCH 04/12] crate publishing --- tools/publish.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/publish.sh b/tools/publish.sh index 6d827feaf6214..f6c3266a9b492 100644 --- a/tools/publish.sh +++ b/tools/publish.sh @@ -30,6 +30,7 @@ crates=( bevy_wgpu bevy_internal bevy_dylib + bevy_geometry ) cd crates From e2fd3d2739d474ecb8f8059969e31c25b308a0e1 Mon Sep 17 00:00:00 2001 From: Aevyrie Roessler Date: Thu, 11 Mar 2021 11:11:18 -0800 Subject: [PATCH 05/12] derives --- crates/bevy_geometry/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/bevy_geometry/src/lib.rs b/crates/bevy_geometry/src/lib.rs index 4906b2cea2400..03271d01832ac 100644 --- a/crates/bevy_geometry/src/lib.rs +++ b/crates/bevy_geometry/src/lib.rs @@ -2,22 +2,26 @@ use bevy_math::{Quat, Vec3}; pub trait Primitive3d {} +#[derive(Copy, Clone, PartialEq, Debug)] pub struct Sphere { pub origin: Vec3, pub radius: f32, } +#[derive(Copy, Clone, PartialEq, Debug)] pub struct Box { pub maximums: Vec3, pub minimums: Vec3, pub orientation: Quat, } +#[derive(Copy, Clone, PartialEq, Debug)] pub struct AxisAlignedBox { pub maximums: Vec3, pub minimums: Vec3, } +#[derive(Copy, Clone, PartialEq, Debug)] pub struct Plane { pub point: Vec3, pub normal: Vec3, From a9b50729e99148633e3eee29c1f4c71e1d3af5de Mon Sep 17 00:00:00 2001 From: Aevyrie Roessler Date: Thu, 11 Mar 2021 12:43:07 -0800 Subject: [PATCH 06/12] correct publish order --- tools/publish.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/publish.sh b/tools/publish.sh index f6c3266a9b492..188d04b0e25d1 100644 --- a/tools/publish.sh +++ b/tools/publish.sh @@ -3,6 +3,7 @@ crates=( bevy_utils bevy_derive bevy_math + bevy_geometry bevy_tasks bevy_ecs/macros bevy_ecs @@ -30,7 +31,6 @@ crates=( bevy_wgpu bevy_internal bevy_dylib - bevy_geometry ) cd crates From 432138fce00c401f20a6957b74da0b9d97861009 Mon Sep 17 00:00:00 2001 From: Aevyrie Roessler Date: Fri, 12 Mar 2021 00:21:22 -0800 Subject: [PATCH 07/12] add all the things --- crates/bevy_geometry/Cargo.toml | 3 +- crates/bevy_geometry/src/lib.rs | 179 +++++++++++++++++++++++++++++--- tools/publish.sh | 2 +- 3 files changed, 170 insertions(+), 14 deletions(-) diff --git a/crates/bevy_geometry/Cargo.toml b/crates/bevy_geometry/Cargo.toml index 62aab1c1d29e1..fe6d31bd2b12b 100644 --- a/crates/bevy_geometry/Cargo.toml +++ b/crates/bevy_geometry/Cargo.toml @@ -10,5 +10,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bevy_transform = { path = "../bevy_transform", version = "0.4.0" } bevy_math = { path = "../bevy_math", version = "0.4.0" } -bevy_reflect = { path = "../bevy_reflect", version = "0.4.0" } +bevy_reflect = { path = "../bevy_reflect", version = "0.4.0", features = ["bevy"] } diff --git a/crates/bevy_geometry/src/lib.rs b/crates/bevy_geometry/src/lib.rs index 03271d01832ac..83c6db4950267 100644 --- a/crates/bevy_geometry/src/lib.rs +++ b/crates/bevy_geometry/src/lib.rs @@ -1,28 +1,183 @@ -use bevy_math::{Quat, Vec3}; +use bevy_math::*; +use bevy_reflect::Reflect; +//use bevy_transform::components::GlobalTransform; +use std::error::Error; +use std::fmt; -pub trait Primitive3d {} +pub trait Primitive3d { + /* + /// Returns true if this primitive is on the outside (normal direction) of the supplied + fn outside_plane( + &self, + primitive_transform: GlobalTransform, + plane: Plane, + plane_transform: GlobalTransform, + ) -> bool;*/ +} -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Debug, Clone)] +pub enum PrimitiveError { + MinGreaterThanMax, + NonPositiveExtents, +} +impl Error for PrimitiveError {} +impl fmt::Display for PrimitiveError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + PrimitiveError::MinGreaterThanMax => { + write!(f, "AxisAlignedBox minimums must be smaller than maximums") + } + PrimitiveError::NonPositiveExtents => { + write!(f, "AxisAlignedBox extents must be greater than zero") + } + } + } +} + +#[derive(Copy, Clone, PartialEq, Debug, Reflect)] pub struct Sphere { pub origin: Vec3, pub radius: f32, } +impl Primitive3d for Sphere {} -#[derive(Copy, Clone, PartialEq, Debug)] -pub struct Box { - pub maximums: Vec3, - pub minimums: Vec3, +/// An oriented box, unlike an axis aligned box, can be rotated and is not constrained to match the +/// orientation of the coordinate system it is defined in. Internally, this is represented as an +/// axis aligned box with some rotation ([Quat]) applied. +#[derive(Copy, Clone, PartialEq, Debug, Reflect)] +pub struct OrientedBox { + pub aab: AxisAlignedBox, pub orientation: Quat, } +impl Primitive3d for OrientedBox {} -#[derive(Copy, Clone, PartialEq, Debug)] +/// An axis aligned box is a box whose axes lie in the x/y/z directions of the coordinate system +/// the box is defined in. +#[derive(Copy, Clone, PartialEq, Debug, Reflect)] pub struct AxisAlignedBox { - pub maximums: Vec3, - pub minimums: Vec3, + minimums: Vec3, + maximums: Vec3, } +impl Primitive3d for AxisAlignedBox {} +impl AxisAlignedBox { + pub fn from_min_max(minimums: Vec3, maximums: Vec3) -> Result { + if (maximums - minimums).min_element() > 0.0 { + Ok(AxisAlignedBox { minimums, maximums }) + } else { + Err(PrimitiveError::MinGreaterThanMax) + } + } + pub fn from_extents_origin( + extents: Vec3, + origin: Vec3, + ) -> Result { + if extents.min_element() > 0.0 { + Ok(AxisAlignedBox { + minimums: origin, + maximums: extents + origin, + }) + } else { + Err(PrimitiveError::NonPositiveExtents) + } + } +} + +/// A frustum is a truncated pyramid that is used to represent the "volume" of world space that is +/// visible to the camera. +#[derive(Copy, Clone, PartialEq, Debug, Reflect)] +#[reflect_value(PartialEq)] +pub struct Frustum { + planes: [Plane; 6], +} +impl Primitive3d for Frustum {} +impl Frustum { + pub fn from_camera_properties( + &self, + camera_position: Mat4, + projection_matrix: Mat4, + ) -> Frustum { + let ndc_to_world: Mat4 = camera_position * projection_matrix.inverse(); + // Near/Far, Top/Bottom, Left/Right + let nbl_world = ndc_to_world.project_point3(Vec3::new(-1.0, -1.0, -1.0)); + let nbr_world = ndc_to_world.project_point3(Vec3::new(1.0, -1.0, -1.0)); + let ntl_world = ndc_to_world.project_point3(Vec3::new(-1.0, 1.0, -1.0)); + let fbl_world = ndc_to_world.project_point3(Vec3::new(-1.0, -1.0, 1.0)); + let ftr_world = ndc_to_world.project_point3(Vec3::new(1.0, 1.0, 1.0)); + let ftl_world = ndc_to_world.project_point3(Vec3::new(-1.0, 1.0, 1.0)); + let fbr_world = ndc_to_world.project_point3(Vec3::new(1.0, -1.0, 1.0)); + let ntr_world = ndc_to_world.project_point3(Vec3::new(1.0, 1.0, -1.0)); + + let near_normal = (nbr_world - nbl_world) + .cross(ntl_world - nbl_world) + .normalize(); + let far_normal = (fbr_world - ftr_world) + .cross(ftl_world - ftr_world) + .normalize(); + let top_normal = (ftl_world - ftr_world) + .cross(ntr_world - ftr_world) + .normalize(); + let bottom_normal = (fbl_world - nbl_world) + .cross(nbr_world - nbl_world) + .normalize(); + let right_normal = (ntr_world - ftr_world) + .cross(fbr_world - ftr_world) + .normalize(); + let left_normal = (ntl_world - nbl_world) + .cross(fbl_world - nbl_world) + .normalize(); + let left = Plane { + point: nbl_world, + normal: left_normal, + }; + let right = Plane { + point: ftr_world, + normal: right_normal, + }; + let bottom = Plane { + point: nbl_world, + normal: bottom_normal, + }; + let top = Plane { + point: ftr_world, + normal: top_normal, + }; + let near = Plane { + point: nbl_world, + normal: near_normal, + }; + let far = Plane { + point: ftr_world, + normal: far_normal, + }; + Frustum { + planes: [left, right, top, bottom, near, far], + } + } +} + +/// A plane is defined by a point in space and a normal vector at that point. #[derive(Copy, Clone, PartialEq, Debug)] pub struct Plane { - pub point: Vec3, - pub normal: Vec3, + point: Vec3, + normal: Vec3, +} +impl Primitive3d for Plane {} +impl Plane { + /// Generate a plane from three points that lie on the plane. + pub fn from_points(points: [Vec3; 3]) -> Plane { + let point = points[1]; + let arm_1 = points[0] - point; + let arm_2 = points[2] - point; + let normal = arm_1.cross(arm_2).normalize(); + Plane { point, normal } + } + /// Generate a plane from a point on that plane and the normal direction of the plane. The + /// normal vector does not need to be normalized (length can be != 1). + pub fn from_point_normal(point: Vec3, normal: Vec3) -> Plane { + Plane { + point, + normal: normal.normalize(), + } + } } diff --git a/tools/publish.sh b/tools/publish.sh index 188d04b0e25d1..a760824cf45f5 100644 --- a/tools/publish.sh +++ b/tools/publish.sh @@ -3,7 +3,6 @@ crates=( bevy_utils bevy_derive bevy_math - bevy_geometry bevy_tasks bevy_ecs/macros bevy_ecs @@ -17,6 +16,7 @@ crates=( bevy_core bevy_diagnostic bevy_transform + bevy_geometry bevy_window bevy_render bevy_input From 45ba723ea483a286725f098ac09c10dc2a4ea4ac Mon Sep 17 00:00:00 2001 From: Aevyrie Roessler Date: Sat, 13 Mar 2021 09:59:11 -0800 Subject: [PATCH 08/12] Adding helper methods --- crates/bevy_geometry/src/lib.rs | 264 ++++++++++++++++++++++++++------ tools/publish.sh | 2 +- 2 files changed, 221 insertions(+), 45 deletions(-) diff --git a/crates/bevy_geometry/src/lib.rs b/crates/bevy_geometry/src/lib.rs index 83c6db4950267..1dcd10406c67b 100644 --- a/crates/bevy_geometry/src/lib.rs +++ b/crates/bevy_geometry/src/lib.rs @@ -1,18 +1,12 @@ use bevy_math::*; use bevy_reflect::Reflect; -//use bevy_transform::components::GlobalTransform; use std::error::Error; use std::fmt; pub trait Primitive3d { - /* - /// Returns true if this primitive is on the outside (normal direction) of the supplied - fn outside_plane( - &self, - primitive_transform: GlobalTransform, - plane: Plane, - plane_transform: GlobalTransform, - ) -> bool;*/ + /// Returns true if this primitive is entirely on the outside (in the normal direction) of the + /// supplied plane. + fn outside_plane(&self, plane: Plane) -> bool; } #[derive(Debug, Clone)] @@ -25,7 +19,10 @@ impl fmt::Display for PrimitiveError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { PrimitiveError::MinGreaterThanMax => { - write!(f, "AxisAlignedBox minimums must be smaller than maximums") + write!( + f, + "AxisAlignedBox minimums must be smaller or equal to the maximums" + ) } PrimitiveError::NonPositiveExtents => { write!(f, "AxisAlignedBox extents must be greater than zero") @@ -36,76 +33,214 @@ impl fmt::Display for PrimitiveError { #[derive(Copy, Clone, PartialEq, Debug, Reflect)] pub struct Sphere { - pub origin: Vec3, - pub radius: f32, + origin: Vec3, + radius: f32, +} + +impl Sphere { + /// Get a reference to the sphere's origin. + pub fn origin(&self) -> &Vec3 { + &self.origin + } + + /// Get a reference to the sphere's radius. + pub fn radius(&self) -> &f32 { + &self.radius + } + + /// Set the sphere's origin. + pub fn set_origin(&mut self, origin: Vec3) { + self.origin = origin; + } + + /// Set the sphere's radius. + pub fn set_radius(&mut self, radius: f32) { + self.radius = radius; + } +} +impl Primitive3d for Sphere { + /// Use the sphere's position and radius to determin eif it is entirely on the outside of the + /// the supplied plane. + fn outside_plane(&self, plane: Plane) -> bool { + plane.distance_to_point(&self.origin) > self.radius + } } -impl Primitive3d for Sphere {} /// An oriented box, unlike an axis aligned box, can be rotated and is not constrained to match the /// orientation of the coordinate system it is defined in. Internally, this is represented as an /// axis aligned box with some rotation ([Quat]) applied. #[derive(Copy, Clone, PartialEq, Debug, Reflect)] pub struct OrientedBox { - pub aab: AxisAlignedBox, - pub orientation: Quat, + aab: AxisAlignedBox, + transform: Mat4, +} +impl Primitive3d for OrientedBox { + fn outside_plane(&self, plane: Plane) -> bool { + for vertex in self.vertices().iter() { + if plane.distance_to_point(vertex) <= 0.0 { + return false; + } + } + true + } +} +impl OrientedBox { + /// An ordered list of the vertices that form the 8 corners of the [AxisAlignedBox]. + /// ```none + /// (5)------(1) + /// | \ | \ + /// | (4)------(0) + /// | | | | + /// (7)--|---(3) | + /// \ | \ | + /// (6)------(2) + /// ``` + pub fn vertices(&self) -> [Vec3; 8] { + let mut vertices = [Vec3::ZERO; 8]; + let aab_vertices = self.aab.vertices(); + for i in 0..vertices.len() { + vertices[i] = self.transform.project_point3(aab_vertices[i]) + } + vertices + } + + /// Set the oriented box's aab. + pub fn set_aab(&mut self, aab: AxisAlignedBox) { + self.aab = aab; + } + + /// Set the oriented box's transform. + pub fn set_transform(&mut self, transform: Mat4) { + self.transform = transform; + } + pub fn fast_aabb(&self) -> AxisAlignedBox { + let vertices = self.vertices(); + let mut max = Vec3::splat(f32::MIN); + let mut min = Vec3::splat(f32::MAX); + for vertex in vertices.iter() { + max = vertex.max(max); + min = vertex.min(min); + } + // Unwrap is okay here because min < max + AxisAlignedBox::from_min_max(min, max).unwrap() + } } -impl Primitive3d for OrientedBox {} /// An axis aligned box is a box whose axes lie in the x/y/z directions of the coordinate system /// the box is defined in. #[derive(Copy, Clone, PartialEq, Debug, Reflect)] pub struct AxisAlignedBox { - minimums: Vec3, - maximums: Vec3, + min: Vec3, + max: Vec3, +} +impl Primitive3d for AxisAlignedBox { + fn outside_plane(&self, plane: Plane) -> bool { + for vertex in self.vertices().iter() { + if plane.distance_to_point(vertex) <= 0.0 { + return false; + } + } + true + } } -impl Primitive3d for AxisAlignedBox {} impl AxisAlignedBox { - pub fn from_min_max(minimums: Vec3, maximums: Vec3) -> Result { - if (maximums - minimums).min_element() > 0.0 { - Ok(AxisAlignedBox { minimums, maximums }) + /// An ordered list of the vertices that form the 8 corners of the [AxisAlignedBox]. + /// ```none + /// (5)------(1) Y + /// | \ | \ | + /// | (4)------(0) MAX o---X + /// | | | | \ + /// MIN (7)--|---(3) | Z + /// \ | \ | + /// (6)------(2) + /// ``` + pub fn vertices(&self) -> [Vec3; 8] { + let min = self.min; + let max = self.max; + [ + Vec3::new(max.x, max.y, max.z), + Vec3::new(max.x, max.y, min.z), + Vec3::new(max.x, min.y, max.z), + Vec3::new(max.x, min.y, min.z), + Vec3::new(min.x, max.y, max.z), + Vec3::new(min.x, max.y, min.z), + Vec3::new(min.x, min.y, max.z), + Vec3::new(min.x, min.y, min.z), + ] + } + /// Construct an [AxisAlignedBox] given the coordinates of the minimum and maximum corners. + pub fn from_min_max(min: Vec3, max: Vec3) -> Result { + if (max - min).min_element() >= 0.0 { + Ok(AxisAlignedBox { min, max }) } else { Err(PrimitiveError::MinGreaterThanMax) } } + /// Construct an [AxisALignedBox] from the origin at the minimum corner, and the extents - the + /// dimensions of the box in each axis. pub fn from_extents_origin( extents: Vec3, origin: Vec3, ) -> Result { if extents.min_element() > 0.0 { Ok(AxisAlignedBox { - minimums: origin, - maximums: extents + origin, + min: origin, + max: extents + origin, }) } else { Err(PrimitiveError::NonPositiveExtents) } } + /// Computes the AAB that + pub fn from_points(points: Vec) -> AxisAlignedBox { + let mut max = Vec3::splat(f32::MIN); + let mut min = Vec3::splat(f32::MAX); + for &point in points.iter() { + max = point.max(max); + min = point.min(min); + } + // Unwrap is okay here because min < max + AxisAlignedBox::from_min_max(min, max).unwrap() + } } -/// A frustum is a truncated pyramid that is used to represent the "volume" of world space that is +/// A frustum is a truncated pyramid that is used to represent the volume of world space that is /// visible to the camera. #[derive(Copy, Clone, PartialEq, Debug, Reflect)] #[reflect_value(PartialEq)] pub struct Frustum { planes: [Plane; 6], + vertices: [Vec3; 8], +} +impl Primitive3d for Frustum { + fn outside_plane(&self, plane: Plane) -> bool { + for vertex in self.vertices().iter() { + if plane.distance_to_point(vertex) <= 0.0 { + return false; + } + } + true + } } -impl Primitive3d for Frustum {} impl Frustum { - pub fn from_camera_properties( - &self, - camera_position: Mat4, - projection_matrix: Mat4, - ) -> Frustum { + fn compute_vertices(camera_position: Mat4, projection_matrix: Mat4) -> [Vec3; 8] { let ndc_to_world: Mat4 = camera_position * projection_matrix.inverse(); - // Near/Far, Top/Bottom, Left/Right - let nbl_world = ndc_to_world.project_point3(Vec3::new(-1.0, -1.0, -1.0)); - let nbr_world = ndc_to_world.project_point3(Vec3::new(1.0, -1.0, -1.0)); - let ntl_world = ndc_to_world.project_point3(Vec3::new(-1.0, 1.0, -1.0)); - let fbl_world = ndc_to_world.project_point3(Vec3::new(-1.0, -1.0, 1.0)); - let ftr_world = ndc_to_world.project_point3(Vec3::new(1.0, 1.0, 1.0)); - let ftl_world = ndc_to_world.project_point3(Vec3::new(-1.0, 1.0, 1.0)); - let fbr_world = ndc_to_world.project_point3(Vec3::new(1.0, -1.0, 1.0)); - let ntr_world = ndc_to_world.project_point3(Vec3::new(1.0, 1.0, -1.0)); + [ + ndc_to_world.project_point3(Vec3::new(-1.0, -1.0, -1.0)), + ndc_to_world.project_point3(Vec3::new(1.0, -1.0, -1.0)), + ndc_to_world.project_point3(Vec3::new(-1.0, 1.0, -1.0)), + ndc_to_world.project_point3(Vec3::new(1.0, 1.0, -1.0)), + ndc_to_world.project_point3(Vec3::new(-1.0, -1.0, 1.0)), + ndc_to_world.project_point3(Vec3::new(1.0, -1.0, 1.0)), + ndc_to_world.project_point3(Vec3::new(-1.0, 1.0, 1.0)), + ndc_to_world.project_point3(Vec3::new(1.0, 1.0, 1.0)), + ] + } + + pub fn from_camera_properties(camera_position: Mat4, projection_matrix: Mat4) -> Frustum { + let vertices = Frustum::compute_vertices(camera_position, projection_matrix); + let [nbl_world, nbr_world, ntl_world, ntr_world, fbl_world, fbr_world, ftl_world, ftr_world] = + vertices; let near_normal = (nbr_world - nbl_world) .cross(ntl_world - nbl_world) @@ -150,9 +285,30 @@ impl Frustum { point: ftr_world, normal: far_normal, }; - Frustum { - planes: [left, right, top, bottom, near, far], - } + + let planes = [left, right, top, bottom, near, far]; + + Frustum { planes, vertices } + } + + /// Get a reference to the frustum's vertices. These are given as an ordered list of vertices + /// that form the 8 corners of a [Frustum]. + /// ```none + /// (6)--------------(7) + /// | \ TOP / | + /// | (2)------(3) | + /// | L | | R | + /// (4) | NEAR | (5) + /// \ | | / + /// (0)------(1) + /// ``` + pub fn vertices(&self) -> &[Vec3; 8] { + &self.vertices + } + + /// Get a reference to the frustum's planes. + pub fn planes(&self) -> &[Plane; 6] { + &self.planes } } @@ -162,7 +318,11 @@ pub struct Plane { point: Vec3, normal: Vec3, } -impl Primitive3d for Plane {} +impl Primitive3d for Plane { + fn outside_plane(&self, plane: Plane) -> bool { + self.normal == plane.normal && self.distance_to_point(plane.point()) > 0.0 + } +} impl Plane { /// Generate a plane from three points that lie on the plane. pub fn from_points(points: [Vec3; 3]) -> Plane { @@ -180,4 +340,20 @@ impl Plane { normal: normal.normalize(), } } + /// Returns the nearest distance from the supplied point to this plane. Positive values are in + /// the direction of the plane's normal (outside), negative values are opposite the direction + /// of the planes normal (inside). + pub fn distance_to_point(&self, point: &Vec3) -> f32 { + self.normal.dot(*point) + -self.normal.dot(self.point) + } + + /// Get a reference to the plane's point. + pub fn point(&self) -> &Vec3 { + &self.point + } + + /// Get a reference to the plane's normal. + pub fn normal(&self) -> &Vec3 { + &self.normal + } } diff --git a/tools/publish.sh b/tools/publish.sh index a760824cf45f5..188d04b0e25d1 100644 --- a/tools/publish.sh +++ b/tools/publish.sh @@ -3,6 +3,7 @@ crates=( bevy_utils bevy_derive bevy_math + bevy_geometry bevy_tasks bevy_ecs/macros bevy_ecs @@ -16,7 +17,6 @@ crates=( bevy_core bevy_diagnostic bevy_transform - bevy_geometry bevy_window bevy_render bevy_input From feab0c8a5c5e36d1c1c3b9f166620c33f3ed9a94 Mon Sep 17 00:00:00 2001 From: Aevyrie Roessler Date: Thu, 18 Mar 2021 23:49:27 -0700 Subject: [PATCH 09/12] Suggested review changes --- crates/bevy_geometry/src/lib.rs | 57 ++++++++++++++++----------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/crates/bevy_geometry/src/lib.rs b/crates/bevy_geometry/src/lib.rs index 1dcd10406c67b..f7b5cf45310f7 100644 --- a/crates/bevy_geometry/src/lib.rs +++ b/crates/bevy_geometry/src/lib.rs @@ -39,13 +39,13 @@ pub struct Sphere { impl Sphere { /// Get a reference to the sphere's origin. - pub fn origin(&self) -> &Vec3 { - &self.origin + pub fn origin(&self) -> Vec3 { + self.origin } /// Get a reference to the sphere's radius. - pub fn radius(&self) -> &f32 { - &self.radius + pub fn radius(&self) -> f32 { + self.radius } /// Set the sphere's origin. @@ -62,7 +62,7 @@ impl Primitive3d for Sphere { /// Use the sphere's position and radius to determin eif it is entirely on the outside of the /// the supplied plane. fn outside_plane(&self, plane: Plane) -> bool { - plane.distance_to_point(&self.origin) > self.radius + plane.distance_to_point(self.origin) > self.radius } } @@ -70,21 +70,21 @@ impl Primitive3d for Sphere { /// orientation of the coordinate system it is defined in. Internally, this is represented as an /// axis aligned box with some rotation ([Quat]) applied. #[derive(Copy, Clone, PartialEq, Debug, Reflect)] -pub struct OrientedBox { - aab: AxisAlignedBox, +pub struct OBB { + aab: AABB, transform: Mat4, } -impl Primitive3d for OrientedBox { +impl Primitive3d for OBB { fn outside_plane(&self, plane: Plane) -> bool { for vertex in self.vertices().iter() { - if plane.distance_to_point(vertex) <= 0.0 { + if plane.distance_to_point(*vertex) <= 0.0 { return false; } } true } } -impl OrientedBox { +impl OBB { /// An ordered list of the vertices that form the 8 corners of the [AxisAlignedBox]. /// ```none /// (5)------(1) @@ -105,7 +105,7 @@ impl OrientedBox { } /// Set the oriented box's aab. - pub fn set_aab(&mut self, aab: AxisAlignedBox) { + pub fn set_aabb(&mut self, aab: AABB) { self.aab = aab; } @@ -113,7 +113,7 @@ impl OrientedBox { pub fn set_transform(&mut self, transform: Mat4) { self.transform = transform; } - pub fn fast_aabb(&self) -> AxisAlignedBox { + pub fn fast_aabb(&self) -> AABB { let vertices = self.vertices(); let mut max = Vec3::splat(f32::MIN); let mut min = Vec3::splat(f32::MAX); @@ -122,18 +122,18 @@ impl OrientedBox { min = vertex.min(min); } // Unwrap is okay here because min < max - AxisAlignedBox::from_min_max(min, max).unwrap() + AABB::from_min_max(min, max).unwrap() } } /// An axis aligned box is a box whose axes lie in the x/y/z directions of the coordinate system /// the box is defined in. #[derive(Copy, Clone, PartialEq, Debug, Reflect)] -pub struct AxisAlignedBox { +pub struct AABB { min: Vec3, max: Vec3, } -impl Primitive3d for AxisAlignedBox { +impl Primitive3d for AABB { fn outside_plane(&self, plane: Plane) -> bool { for vertex in self.vertices().iter() { if plane.distance_to_point(vertex) <= 0.0 { @@ -143,7 +143,7 @@ impl Primitive3d for AxisAlignedBox { true } } -impl AxisAlignedBox { +impl AABB { /// An ordered list of the vertices that form the 8 corners of the [AxisAlignedBox]. /// ```none /// (5)------(1) Y @@ -169,21 +169,18 @@ impl AxisAlignedBox { ] } /// Construct an [AxisAlignedBox] given the coordinates of the minimum and maximum corners. - pub fn from_min_max(min: Vec3, max: Vec3) -> Result { + pub fn from_min_max(min: Vec3, max: Vec3) -> Result { if (max - min).min_element() >= 0.0 { - Ok(AxisAlignedBox { min, max }) + Ok(AABB { min, max }) } else { Err(PrimitiveError::MinGreaterThanMax) } } /// Construct an [AxisALignedBox] from the origin at the minimum corner, and the extents - the /// dimensions of the box in each axis. - pub fn from_extents_origin( - extents: Vec3, - origin: Vec3, - ) -> Result { + pub fn from_extents_origin(extents: Vec3, origin: Vec3) -> Result { if extents.min_element() > 0.0 { - Ok(AxisAlignedBox { + Ok(AABB { min: origin, max: extents + origin, }) @@ -192,7 +189,7 @@ impl AxisAlignedBox { } } /// Computes the AAB that - pub fn from_points(points: Vec) -> AxisAlignedBox { + pub fn from_points(points: &[Vec3]) -> AABB { let mut max = Vec3::splat(f32::MIN); let mut min = Vec3::splat(f32::MAX); for &point in points.iter() { @@ -200,7 +197,7 @@ impl AxisAlignedBox { min = point.min(min); } // Unwrap is okay here because min < max - AxisAlignedBox::from_min_max(min, max).unwrap() + AABB::from_min_max(min, max).unwrap() } } @@ -223,8 +220,8 @@ impl Primitive3d for Frustum { } } impl Frustum { - fn compute_vertices(camera_position: Mat4, projection_matrix: Mat4) -> [Vec3; 8] { - let ndc_to_world: Mat4 = camera_position * projection_matrix.inverse(); + fn compute_vertices(camera_position: &Mat4, projection_matrix: &Mat4) -> [Vec3; 8] { + let ndc_to_world: Mat4 = *camera_position * projection_matrix.inverse(); [ ndc_to_world.project_point3(Vec3::new(-1.0, -1.0, -1.0)), ndc_to_world.project_point3(Vec3::new(1.0, -1.0, -1.0)), @@ -237,7 +234,7 @@ impl Frustum { ] } - pub fn from_camera_properties(camera_position: Mat4, projection_matrix: Mat4) -> Frustum { + pub fn from_camera_properties(camera_position: &Mat4, projection_matrix: &Mat4) -> Frustum { let vertices = Frustum::compute_vertices(camera_position, projection_matrix); let [nbl_world, nbr_world, ntl_world, ntr_world, fbl_world, fbr_world, ftl_world, ftr_world] = vertices; @@ -343,8 +340,8 @@ impl Plane { /// Returns the nearest distance from the supplied point to this plane. Positive values are in /// the direction of the plane's normal (outside), negative values are opposite the direction /// of the planes normal (inside). - pub fn distance_to_point(&self, point: &Vec3) -> f32 { - self.normal.dot(*point) + -self.normal.dot(self.point) + pub fn distance_to_point(&self, point: Vec3) -> f32 { + self.normal.dot(point) + -self.normal.dot(self.point) } /// Get a reference to the plane's point. From f369c50912162d66a606b39c1616b474c9fb6af7 Mon Sep 17 00:00:00 2001 From: Aevyrie Roessler Date: Thu, 18 Mar 2021 23:50:50 -0700 Subject: [PATCH 10/12] Fix errors and comments --- crates/bevy_geometry/src/lib.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/bevy_geometry/src/lib.rs b/crates/bevy_geometry/src/lib.rs index f7b5cf45310f7..410f852c9b878 100644 --- a/crates/bevy_geometry/src/lib.rs +++ b/crates/bevy_geometry/src/lib.rs @@ -38,12 +38,12 @@ pub struct Sphere { } impl Sphere { - /// Get a reference to the sphere's origin. + /// Get the sphere's origin. pub fn origin(&self) -> Vec3 { self.origin } - /// Get a reference to the sphere's radius. + /// Get the sphere's radius. pub fn radius(&self) -> f32 { self.radius } @@ -136,7 +136,7 @@ pub struct AABB { impl Primitive3d for AABB { fn outside_plane(&self, plane: Plane) -> bool { for vertex in self.vertices().iter() { - if plane.distance_to_point(vertex) <= 0.0 { + if plane.distance_to_point(*vertex) <= 0.0 { return false; } } @@ -212,7 +212,7 @@ pub struct Frustum { impl Primitive3d for Frustum { fn outside_plane(&self, plane: Plane) -> bool { for vertex in self.vertices().iter() { - if plane.distance_to_point(vertex) <= 0.0 { + if plane.distance_to_point(*vertex) <= 0.0 { return false; } } @@ -344,13 +344,13 @@ impl Plane { self.normal.dot(point) + -self.normal.dot(self.point) } - /// Get a reference to the plane's point. - pub fn point(&self) -> &Vec3 { - &self.point + /// Get the plane's point. + pub fn point(&self) -> Vec3 { + self.point } - /// Get a reference to the plane's normal. - pub fn normal(&self) -> &Vec3 { - &self.normal + /// Get the plane's normal. + pub fn normal(&self) -> Vec3 { + self.normal } } From 924cc5a81db3bb678879ae56f24e4499d52a123d Mon Sep 17 00:00:00 2001 From: Aevyrie Date: Thu, 8 Apr 2021 13:07:20 -0700 Subject: [PATCH 11/12] Update crates/bevy_geometry/src/lib.rs Co-authored-by: Grindv1k --- crates/bevy_geometry/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_geometry/src/lib.rs b/crates/bevy_geometry/src/lib.rs index 410f852c9b878..693531e7577d6 100644 --- a/crates/bevy_geometry/src/lib.rs +++ b/crates/bevy_geometry/src/lib.rs @@ -176,7 +176,7 @@ impl AABB { Err(PrimitiveError::MinGreaterThanMax) } } - /// Construct an [AxisALignedBox] from the origin at the minimum corner, and the extents - the + /// Construct an [AxisAlignedBox] from the origin at the minimum corner, and the extents - the /// dimensions of the box in each axis. pub fn from_extents_origin(extents: Vec3, origin: Vec3) -> Result { if extents.min_element() > 0.0 { From e967a83bd7eb3e4f558d6d0005258e7a8fb51ac8 Mon Sep 17 00:00:00 2001 From: Aevyrie Date: Thu, 8 Apr 2021 13:07:59 -0700 Subject: [PATCH 12/12] Apply suggestions from code review Co-authored-by: Grindv1k --- crates/bevy_geometry/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_geometry/src/lib.rs b/crates/bevy_geometry/src/lib.rs index 693531e7577d6..8980d5c12a3f7 100644 --- a/crates/bevy_geometry/src/lib.rs +++ b/crates/bevy_geometry/src/lib.rs @@ -59,7 +59,7 @@ impl Sphere { } } impl Primitive3d for Sphere { - /// Use the sphere's position and radius to determin eif it is entirely on the outside of the + /// Use the sphere's position and radius to determine if it is entirely on the outside of the /// the supplied plane. fn outside_plane(&self, plane: Plane) -> bool { plane.distance_to_point(self.origin) > self.radius @@ -188,7 +188,7 @@ impl AABB { Err(PrimitiveError::NonPositiveExtents) } } - /// Computes the AAB that + /// Computes the [AxisAlignedBox] whose extents are determined by the minimum and maximum of the points given. pub fn from_points(points: &[Vec3]) -> AABB { let mut max = Vec3::splat(f32::MIN); let mut min = Vec3::splat(f32::MAX);