diff --git a/crates/bevy_render/src/mesh/mesh/mod.rs b/crates/bevy_render/src/mesh/mesh/mod.rs index 84ad6eb0de..49c8757691 100644 --- a/crates/bevy_render/src/mesh/mesh/mod.rs +++ b/crates/bevy_render/src/mesh/mesh/mod.rs @@ -464,27 +464,13 @@ impl Mesh { /// Compute the Axis-Aligned Bounding Box of the mesh vertices in model space pub fn compute_aabb(&self) -> Option { - if let Some(VertexAttributeValues::Float32x3(values)) = + let Some(VertexAttributeValues::Float32x3(values)) = self.attribute(Mesh::ATTRIBUTE_POSITION) - { - let mut minimum = VEC3_MAX; - let mut maximum = VEC3_MIN; - for p in values { - minimum = minimum.min(Vec3::from_slice(p)); - maximum = maximum.max(Vec3::from_slice(p)); - } - if minimum.x != std::f32::MAX - && minimum.y != std::f32::MAX - && minimum.z != std::f32::MAX - && maximum.x != std::f32::MIN - && maximum.y != std::f32::MIN - && maximum.z != std::f32::MIN - { - return Some(Aabb::from_min_max(minimum, maximum)); - } - } + else { + return None; + }; - None + Aabb::enclosing(values.iter().map(|p| Vec3::from_slice(p))) } /// Whether this mesh has morph targets. @@ -635,9 +621,6 @@ struct MeshAttributeData { values: VertexAttributeValues, } -const VEC3_MIN: Vec3 = Vec3::splat(std::f32::MIN); -const VEC3_MAX: Vec3 = Vec3::splat(std::f32::MAX); - fn face_normal(a: [f32; 3], b: [f32; 3], c: [f32; 3]) -> [f32; 3] { let (a, b, c) = (Vec3::from(a), Vec3::from(b), Vec3::from(c)); (b - a).cross(c - a).normalize().into() diff --git a/crates/bevy_render/src/primitives/mod.rs b/crates/bevy_render/src/primitives/mod.rs index d150fe08e9..8b74959dc6 100644 --- a/crates/bevy_render/src/primitives/mod.rs +++ b/crates/bevy_render/src/primitives/mod.rs @@ -1,3 +1,5 @@ +use std::borrow::Borrow; + use bevy_ecs::{component::Component, prelude::Entity, reflect::ReflectComponent}; use bevy_math::{Affine3A, Mat3A, Mat4, Vec3, Vec3A, Vec4, Vec4Swizzles}; use bevy_reflect::Reflect; @@ -29,7 +31,7 @@ use bevy_utils::HashMap; /// [`CalculateBounds`]: crate::view::visibility::VisibilitySystems::CalculateBounds /// [`Mesh`]: crate::mesh::Mesh /// [`Handle`]: crate::mesh::Mesh -#[derive(Component, Clone, Copy, Debug, Default, Reflect)] +#[derive(Component, Clone, Copy, Debug, Default, Reflect, PartialEq)] #[reflect(Component)] pub struct Aabb { pub center: Vec3A, @@ -49,6 +51,30 @@ impl Aabb { } } + /// Returns a bounding box enclosing the specified set of points. + /// + /// Returns `None` if the iterator is empty. + /// + /// # Examples + /// + /// ``` + /// # use bevy_math::{Vec3, Vec3A}; + /// # use bevy_render::primitives::Aabb; + /// let bb = Aabb::enclosing([Vec3::X, Vec3::Z * 2.0, Vec3::Y * -0.5]).unwrap(); + /// assert_eq!(bb.min(), Vec3A::new(0.0, -0.5, 0.0)); + /// assert_eq!(bb.max(), Vec3A::new(1.0, 0.0, 2.0)); + /// ``` + pub fn enclosing>(iter: impl IntoIterator) -> Option { + let mut iter = iter.into_iter().map(|p| *p.borrow()); + let mut min = iter.next()?; + let mut max = min; + for v in iter { + min = Vec3::min(min, v); + max = Vec3::max(max, v); + } + Some(Self::from_min_max(min, max)) + } + /// Calculate the relative radius of the AABB with respect to a plane #[inline] pub fn relative_radius(&self, p_normal: &Vec3A, model: &Mat3A) -> f32 { @@ -455,4 +481,28 @@ mod tests { }; assert!(frustum.intersects_sphere(&sphere, true)); } + + #[test] + fn aabb_enclosing() { + assert_eq!(Aabb::enclosing(<[Vec3; 0]>::default()), None); + assert_eq!( + Aabb::enclosing(vec![Vec3::ONE]).unwrap(), + Aabb::from_min_max(Vec3::ONE, Vec3::ONE) + ); + assert_eq!( + Aabb::enclosing(&[Vec3::Y, Vec3::X, Vec3::Z][..]).unwrap(), + Aabb::from_min_max(Vec3::ZERO, Vec3::ONE) + ); + assert_eq!( + Aabb::enclosing([ + Vec3::NEG_X, + Vec3::X * 2.0, + Vec3::NEG_Y * 5.0, + Vec3::Z, + Vec3::ZERO + ]) + .unwrap(), + Aabb::from_min_max(Vec3::new(-1.0, -5.0, 0.0), Vec3::new(2.0, 0.0, 1.0)) + ); + } }