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
pub fn compute_aabb(&self) -> Option<Aabb> {
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()

View file

@ -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<Mesh>`]: 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<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
#[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))
);
}
}