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:
parent
36eedbfa92
commit
23598d7bec
@ -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()
|
||||||
|
|||||||
@ -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))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user