Add a method to compute a bounding box enclosing a set of points (#9630)

# Objective

Make it easier to create bounding boxes in user code by providing a
constructor that computes a box surrounding an arbitrary number of
points.

## Solution

Add `Aabb::enclosing`, which accepts iterators, slices, or arrays.

---------

Co-authored-by: Tristan Guichaoua <33934311+tguichaoua@users.noreply.github.com>
This commit is contained in:
Joseph 2023-08-30 18:33:13 -07:00 committed by GitHub
parent 36eedbfa92
commit 23598d7bec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 23 deletions

View File

@ -464,27 +464,13 @@ impl Mesh {
/// Compute the Axis-Aligned Bounding Box of the mesh vertices in model space /// Compute the Axis-Aligned Bounding Box of the mesh vertices in model space
pub fn compute_aabb(&self) -> Option<Aabb> { pub fn compute_aabb(&self) -> Option<Aabb> {
if let Some(VertexAttributeValues::Float32x3(values)) = let Some(VertexAttributeValues::Float32x3(values)) =
self.attribute(Mesh::ATTRIBUTE_POSITION) self.attribute(Mesh::ATTRIBUTE_POSITION)
{ else {
let mut minimum = VEC3_MAX; return None;
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));
}
}
None Aabb::enclosing(values.iter().map(|p| Vec3::from_slice(p)))
} }
/// Whether this mesh has morph targets. /// Whether this mesh has morph targets.
@ -635,9 +621,6 @@ struct MeshAttributeData {
values: VertexAttributeValues, 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] { 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)); let (a, b, c) = (Vec3::from(a), Vec3::from(b), Vec3::from(c));
(b - a).cross(c - a).normalize().into() (b - a).cross(c - a).normalize().into()

View File

@ -1,3 +1,5 @@
use std::borrow::Borrow;
use bevy_ecs::{component::Component, prelude::Entity, reflect::ReflectComponent}; use bevy_ecs::{component::Component, prelude::Entity, reflect::ReflectComponent};
use bevy_math::{Affine3A, Mat3A, Mat4, Vec3, Vec3A, Vec4, Vec4Swizzles}; use bevy_math::{Affine3A, Mat3A, Mat4, Vec3, Vec3A, Vec4, Vec4Swizzles};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
@ -29,7 +31,7 @@ use bevy_utils::HashMap;
/// [`CalculateBounds`]: crate::view::visibility::VisibilitySystems::CalculateBounds /// [`CalculateBounds`]: crate::view::visibility::VisibilitySystems::CalculateBounds
/// [`Mesh`]: crate::mesh::Mesh /// [`Mesh`]: crate::mesh::Mesh
/// [`Handle<Mesh>`]: crate::mesh::Mesh /// [`Handle<Mesh>`]: crate::mesh::Mesh
#[derive(Component, Clone, Copy, Debug, Default, Reflect)] #[derive(Component, Clone, Copy, Debug, Default, Reflect, PartialEq)]
#[reflect(Component)] #[reflect(Component)]
pub struct Aabb { pub struct Aabb {
pub center: Vec3A, 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<T: Borrow<Vec3>>(iter: impl IntoIterator<Item = T>) -> Option<Self> {
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 /// Calculate the relative radius of the AABB with respect to a plane
#[inline] #[inline]
pub fn relative_radius(&self, p_normal: &Vec3A, model: &Mat3A) -> f32 { pub fn relative_radius(&self, p_normal: &Vec3A, model: &Mat3A) -> f32 {
@ -455,4 +481,28 @@ mod tests {
}; };
assert!(frustum.intersects_sphere(&sphere, true)); 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))
);
}
} }