1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use crate::BoundingVolume;
use bevy::{
prelude::*,
render::{mesh::VertexAttributeValues, pipeline::PrimitiveTopology},
};
use core::panic;
/// Defines a bounding sphere with a radius and an origin at the center.
#[derive(Debug, Clone, Default)]
pub struct BSphere {
/// Origin of the sphere in mesh space. The intent is that the bounding volume will be queried
/// along with its [GlobalTransform], so the origin of the sphere will be transformed to the
/// world position of the mesh, and the radius can be used to determine the bounding volume.
mesh_space_origin: Vec3,
/// Radius of the sphere that bounds the mesh, in mesh space.
mesh_space_radius: f32,
}
impl BSphere {
/// Given the current [GlobalTransform] of the bounded mesh, returns the central origin of the
/// sphere that bounds the mesh in world space.
pub fn origin(&self, transform: GlobalTransform) -> Vec3 {
self.mesh_space_origin + transform.translation
}
/// Given the current [GlobalTransform] of the bounded mesh, returns the radius of the sphere
/// that bounds the mesh in world space.
pub fn radius(&self, transform: &GlobalTransform) -> f32 {
self.mesh_space_radius * transform.scale.max_element()
}
/// Get a reference to the b sphere's mesh space origin.
pub fn mesh_space_origin(&self) -> &Vec3 {
&self.mesh_space_origin
}
/// Get a reference to the b sphere's mesh space radius.
pub fn mesh_space_radius(&self) -> &f32 {
&self.mesh_space_radius
}
}
/// Create a valid boundary sphere from a mesh and globaltransform.
impl BoundingVolume for BSphere {
fn new(mesh: &Mesh, _transform: &GlobalTransform) -> Self {
// Grab a vector of vertex coordinates we can use to iterate through
if mesh.primitive_topology() != PrimitiveTopology::TriangleList {
panic!("Non-TriangleList mesh supplied for bounding sphere 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 point_x = vertices[0];
// Find point y, the point furthest from point x
let point_y = vertices.iter().fold(point_x, |acc, x| {
if x.distance(point_x) >= acc.distance(point_x) {
*x
} else {
acc
}
});
// Find point z, the point furthest from point y
let point_z = vertices.iter().fold(point_y, |acc, x| {
if x.distance(point_y) >= acc.distance(point_y) {
*x
} else {
acc
}
});
// Construct a bounding sphere using these two points as the poles
let mut sphere = BSphere {
mesh_space_origin: point_y.lerp(point_z, 0.5),
mesh_space_radius: point_y.distance(point_z) / 2.0,
};
// Iteratively adjust sphere until it encloses all points
loop {
// Find the furthest point from the origin
let point_n = vertices.iter().fold(point_x, |acc, x| {
if x.distance(sphere.mesh_space_origin) >= acc.distance(sphere.mesh_space_origin) {
*x
} else {
acc
}
});
// If the furthest point is outside the sphere, we need to adjust it
let point_dist = point_n.distance(sphere.mesh_space_origin);
if point_dist > sphere.mesh_space_radius {
let radius_new = (sphere.mesh_space_radius + point_dist) / 2.0;
let lerp_ratio = (point_dist - radius_new) / point_dist;
sphere = BSphere {
mesh_space_origin: sphere.mesh_space_origin.lerp(point_n, lerp_ratio),
mesh_space_radius: radius_new,
};
} else {
return sphere;
}
}
}
/// Generate a debug mesh, and apply the inverse transform. Because the debug mesh is a child,
/// the transform of the parent will be applied to it. This needs to be negated so the bounding
/// circle debug mesh isn't warped.
fn new_debug_mesh(&self, transform: &GlobalTransform) -> Mesh {
let mut mesh = Mesh::from(self);
let inverse_transform = Transform::from_matrix(
Mat4::from_scale_rotation_translation(Vec3::ONE, transform.rotation, Vec3::ZERO)
.inverse(),
);
match mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION) {
None => panic!("Mesh does not contain vertex positions"),
Some(vertex_values) => match vertex_values {
VertexAttributeValues::Float3(ref mut positions) => {
*positions = positions
.iter()
.map(|coordinates| {
inverse_transform.mul_vec3(Vec3::from(*coordinates)).into()
})
.collect()
}
_ => panic!("Unexpected vertex types in ATTRIBUTE_POSITION"),
},
};
mesh
}
fn update_on_transform_change(&self, mesh: &Mesh, transform: &GlobalTransform) -> Option<Self> {
Some(Self::new(mesh, transform))
}
fn outside_plane(
&self,
bound_vol_position: &GlobalTransform,
point: Vec3,
normal: Vec3,
) -> bool {
normal.dot(self.origin(*bound_vol_position)) + -normal.dot(point)
- self.radius(bound_vol_position)
> 0.0
}
}