use crate::aabb::Aabb;
use crate::BoundingVolume;
use bevy::{
prelude::*,
render::{mesh::VertexAttributeValues, pipeline::PrimitiveTopology},
};
use core::panic;
use std::{convert::TryInto, f32::consts::PI};
#[derive(Debug, Clone, Default)]
pub struct Obb {
aabb: Aabb,
mesh_orientation: Quat,
}
impl Obb {
pub fn vertices(&self, transform: GlobalTransform) -> [Vec3; 8] {
let orient = Mat4::from_quat(self.orientation());
let transform = transform.compute_matrix() * orient;
let transform = GlobalTransform::from_matrix(transform);
self.aabb
.vertices_mesh_space()
.iter()
.map(|&vertex| transform * vertex)
.collect::<Vec<Vec3>>()
.as_slice()
.try_into()
.unwrap()
}
pub fn vertices_mesh_space(&self) -> [Vec3; 8] {
let orient = Mat4::from_quat(self.orientation());
let transform = GlobalTransform::from_matrix(orient);
self.aabb.vertices(transform)
}
pub fn from_aabb_orientation(aabb: Aabb, mesh_orientation: Quat) -> Obb {
Obb {
aabb,
mesh_orientation,
}
}
pub fn mesh_aabb(&self) -> &Aabb {
&self.aabb
}
pub fn orientation(&self) -> Quat {
self.mesh_orientation.conjugate()
}
pub fn outer_aabb(&self) -> Aabb {
let axis_aligned_vertices = self.aabb.vertices_mesh_space();
let oriented_vertices: Vec<Vec3> = axis_aligned_vertices
.iter()
.map(|vertex| self.orientation().mul_vec3(*vertex))
.collect();
Aabb::compute_aabb(&oriented_vertices)
}
fn compute_obb(vertices: &[Vec3], orientation: Quat) -> Obb {
let mut maximums = Vec3::new(f32::MIN, f32::MIN, f32::MIN);
let mut minimums = Vec3::new(f32::MAX, f32::MAX, f32::MAX);
let transform = Mat4::from_quat(orientation);
for vertex in vertices.iter() {
maximums = maximums.max(transform.transform_point3(*vertex));
minimums = minimums.min(transform.transform_point3(*vertex));
}
Obb {
aabb: Aabb::from_extents(minimums, maximums),
mesh_orientation: orientation,
}
}
}
impl BoundingVolume for Obb {
fn new(mesh: &Mesh, _transform: &GlobalTransform) -> Self {
if mesh.primitive_topology() != PrimitiveTopology::TriangleList {
panic!("Non-TriangleList mesh supplied for oriented bounding box generation")
}
let vertices: Vec<Vec3> = match mesh.attribute(Mesh::ATTRIBUTE_POSITION) {
None => panic!("Mesh does not contain vertex positions"),
Some(vertex_values) => match &vertex_values {
VertexAttributeValues::Float3(positions) => positions
.iter()
.map(|coordinates| Vec3::from(*coordinates))
.collect(),
_ => panic!("Unexpected vertex types in ATTRIBUTE_POSITION"),
},
};
let mut orientation = Quat::IDENTITY;
let mut volume = f32::MAX;
let orientation_temp = orientation;
for angle in (0..45).step_by(15) {
let new_orientation =
orientation_temp * Quat::from_rotation_y(angle as f32 * 2.0 * PI / 360.0);
let temp_obb = Obb::compute_obb(&vertices, new_orientation);
let diff = temp_obb.mesh_aabb().maximums() - temp_obb.mesh_aabb().minimums();
let new_volume = diff.x * diff.y * diff.z;
if new_volume < volume {
volume = new_volume;
orientation = new_orientation;
}
}
let mut obb = Obb::compute_obb(&vertices, orientation);
let orientation_temp = orientation;
for angle in (0..90).step_by(15) {
let new_orientation =
orientation_temp * Quat::from_rotation_x(angle as f32 * 2.0 * PI / 360.0);
let temp_obb = Obb::compute_obb(&vertices, new_orientation);
let diff = temp_obb.mesh_aabb().maximums() - temp_obb.mesh_aabb().minimums();
let new_volume = diff.x * diff.y * diff.z;
if new_volume < volume {
volume = new_volume;
obb = temp_obb;
}
}
obb
}
fn new_debug_mesh(&self, _transform: &GlobalTransform) -> Mesh {
Mesh::from(self)
}
fn update_on_transform_change(
&self,
_mesh: &Mesh,
_transform: &GlobalTransform,
) -> Option<Self> {
None
}
fn outside_plane(
&self,
bound_vol_position: &GlobalTransform,
point: Vec3,
normal: Vec3,
) -> bool {
for vertex in self.vertices(*bound_vol_position).iter() {
if normal.dot(*vertex) + -normal.dot(point) < 0.0 {
return false;
}
}
true
}
}