1use crate::aabb::Aabb;
2use crate::BoundingVolume;
3use bevy::{
4 prelude::*,
5 render::{mesh::VertexAttributeValues, pipeline::PrimitiveTopology},
6};
7use core::panic;
8use std::{convert::TryInto, f32::consts::PI};
9
10#[derive(Debug, Clone, Default)]
23pub struct Obb {
24 aabb: Aabb,
25 mesh_orientation: Quat,
31}
32
33impl Obb {
34 pub fn vertices(&self, transform: GlobalTransform) -> [Vec3; 8] {
36 let orient = Mat4::from_quat(self.orientation());
37 let transform = transform.compute_matrix() * orient;
38 let transform = GlobalTransform::from_matrix(transform);
39 self.aabb
40 .vertices_mesh_space()
41 .iter()
42 .map(|&vertex| transform * vertex)
43 .collect::<Vec<Vec3>>()
44 .as_slice()
45 .try_into()
46 .unwrap()
47 }
48 pub fn vertices_mesh_space(&self) -> [Vec3; 8] {
49 let orient = Mat4::from_quat(self.orientation());
50 let transform = GlobalTransform::from_matrix(orient);
51 self.aabb.vertices(transform)
52 }
53 pub fn from_aabb_orientation(aabb: Aabb, mesh_orientation: Quat) -> Obb {
54 Obb {
55 aabb,
56 mesh_orientation,
57 }
58 }
59 pub fn mesh_aabb(&self) -> &Aabb {
61 &self.aabb
62 }
63 pub fn orientation(&self) -> Quat {
69 self.mesh_orientation.conjugate()
70 }
71 pub fn outer_aabb(&self) -> Aabb {
82 let axis_aligned_vertices = self.aabb.vertices_mesh_space();
83 let oriented_vertices: Vec<Vec3> = axis_aligned_vertices
84 .iter()
85 .map(|vertex| self.orientation().mul_vec3(*vertex))
86 .collect();
87 Aabb::compute_aabb(&oriented_vertices)
88 }
89 fn compute_obb(vertices: &[Vec3], orientation: Quat) -> Obb {
92 let mut maximums = Vec3::new(f32::MIN, f32::MIN, f32::MIN);
93 let mut minimums = Vec3::new(f32::MAX, f32::MAX, f32::MAX);
94 let transform = Mat4::from_quat(orientation);
95 for vertex in vertices.iter() {
96 maximums = maximums.max(transform.transform_point3(*vertex));
97 minimums = minimums.min(transform.transform_point3(*vertex));
98 }
99 Obb {
100 aabb: Aabb::from_extents(minimums, maximums),
101 mesh_orientation: orientation,
102 }
103 }
104}
105
106impl BoundingVolume for Obb {
107 fn new(mesh: &Mesh, _transform: &GlobalTransform) -> Self {
108 if mesh.primitive_topology() != PrimitiveTopology::TriangleList {
110 panic!("Non-TriangleList mesh supplied for oriented bounding box generation")
111 }
112 let vertices: Vec<Vec3> = match mesh.attribute(Mesh::ATTRIBUTE_POSITION) {
113 None => panic!("Mesh does not contain vertex positions"),
114 Some(vertex_values) => match &vertex_values {
115 VertexAttributeValues::Float3(positions) => positions
116 .iter()
117 .map(|coordinates| Vec3::from(*coordinates))
118 .collect(),
119 _ => panic!("Unexpected vertex types in ATTRIBUTE_POSITION"),
120 },
121 };
122
123 let mut orientation = Quat::IDENTITY;
124 let mut volume = f32::MAX;
125 let orientation_temp = orientation;
127 for angle in (0..45).step_by(15) {
128 let new_orientation =
129 orientation_temp * Quat::from_rotation_y(angle as f32 * 2.0 * PI / 360.0);
130 let temp_obb = Obb::compute_obb(&vertices, new_orientation);
131 let diff = temp_obb.mesh_aabb().maximums() - temp_obb.mesh_aabb().minimums();
132 let new_volume = diff.x * diff.y * diff.z;
133 if new_volume < volume {
134 volume = new_volume;
135 orientation = new_orientation;
136 }
137 }
138 let mut obb = Obb::compute_obb(&vertices, orientation);
139 let orientation_temp = orientation;
140 for angle in (0..90).step_by(15) {
141 let new_orientation =
142 orientation_temp * Quat::from_rotation_x(angle as f32 * 2.0 * PI / 360.0);
143 let temp_obb = Obb::compute_obb(&vertices, new_orientation);
144 let diff = temp_obb.mesh_aabb().maximums() - temp_obb.mesh_aabb().minimums();
145 let new_volume = diff.x * diff.y * diff.z;
146 if new_volume < volume {
147 volume = new_volume;
148 obb = temp_obb;
149 }
150 }
151 obb
152 }
153
154 fn new_debug_mesh(&self, _transform: &GlobalTransform) -> Mesh {
155 Mesh::from(self)
156 }
157
158 fn update_on_transform_change(
159 &self,
160 _mesh: &Mesh,
161 _transform: &GlobalTransform,
162 ) -> Option<Self> {
163 None
165 }
166
167 fn outside_plane(
168 &self,
169 bound_vol_position: &GlobalTransform,
170 point: Vec3,
171 normal: Vec3,
172 ) -> bool {
173 for vertex in self.vertices(*bound_vol_position).iter() {
174 if normal.dot(*vertex) + -normal.dot(point) < 0.0 {
175 return false;
177 }
178 }
179 true
180 }
181}