use crate::assets::MeshAsset;
use crate::rendering::debug_overlay::RenderDebugOverlay;
pub fn add_axis(
overlay: &mut RenderDebugOverlay,
origin: [f32; 3],
direction: [f32; 3],
length: f32,
color: [f32; 4],
) {
let length = length.max(0.01);
let end = [
origin[0] + direction[0] * length,
origin[1] + direction[1] * length,
origin[2] + direction[2] * length,
];
overlay.line_on_top(origin, end, color, 4.0);
let reference = if direction[1].abs() < 0.9 {
[0.0, 1.0, 0.0]
} else {
[1.0, 0.0, 0.0]
};
let side = normalize(cross(direction, reference));
let head_length = length * 0.16;
let head_width = length * 0.08;
for sign in [-1.0, 1.0] {
overlay.line_on_top(
end,
[
end[0] - direction[0] * head_length
+ side[0] * head_width * sign,
end[1] - direction[1] * head_length
+ side[1] * head_width * sign,
end[2] - direction[2] * head_length
+ side[2] * head_width * sign,
],
color,
4.0,
);
}
}
fn cross(left: [f32; 3], right: [f32; 3]) -> [f32; 3] {
[
left[1] * right[2] - left[2] * right[1],
left[2] * right[0] - left[0] * right[2],
left[0] * right[1] - left[1] * right[0],
]
}
fn normalize(value: [f32; 3]) -> [f32; 3] {
let length =
(value[0] * value[0] + value[1] * value[1] + value[2] * value[2])
.sqrt();
if length > f32::EPSILON {
[value[0] / length, value[1] / length, value[2] / length]
} else {
[1.0, 0.0, 0.0]
}
}
pub fn mesh_bounds(mesh: &MeshAsset) -> Option<([f32; 3], [f32; 3])> {
let first = mesh.vertices.first()?.position;
let mut minimum = first;
let mut maximum = first;
for vertex in &mesh.vertices[1..] {
for axis in 0..3 {
minimum[axis] = minimum[axis].min(vertex.position[axis]);
maximum[axis] = maximum[axis].max(vertex.position[axis]);
}
}
Some((minimum, maximum))
}
pub fn mesh_world_radius_from_origin(
mesh: &MeshAsset,
matrix: [[f32; 4]; 4],
) -> Option<f32> {
let (minimum, maximum) = mesh_bounds(mesh)?;
let origin = [matrix[3][0], matrix[3][1], matrix[3][2]];
let mut radius: f32 = 0.0;
for x in [minimum[0], maximum[0]] {
for y in [minimum[1], maximum[1]] {
for z in [minimum[2], maximum[2]] {
let corner = transform_point(matrix, [x, y, z]);
let offset = [
corner[0] - origin[0],
corner[1] - origin[1],
corner[2] - origin[2],
];
radius = radius.max(
(offset[0] * offset[0]
+ offset[1] * offset[1]
+ offset[2] * offset[2])
.sqrt(),
);
}
}
}
Some(radius)
}
pub fn add_bound_box(
overlay: &mut RenderDebugOverlay,
matrix: [[f32; 4]; 4],
minimum: [f32; 3],
maximum: [f32; 3],
color: [f32; 4],
) {
let corners = [
[minimum[0], minimum[1], minimum[2]],
[maximum[0], minimum[1], minimum[2]],
[maximum[0], maximum[1], minimum[2]],
[minimum[0], maximum[1], minimum[2]],
[minimum[0], minimum[1], maximum[2]],
[maximum[0], minimum[1], maximum[2]],
[maximum[0], maximum[1], maximum[2]],
[minimum[0], maximum[1], maximum[2]],
]
.map(|corner| transform_point(matrix, corner));
for (start, end) in [
(0, 1),
(1, 2),
(2, 3),
(3, 0),
(4, 5),
(5, 6),
(6, 7),
(7, 4),
(0, 4),
(1, 5),
(2, 6),
(3, 7),
] {
overlay.line(corners[start], corners[end], color);
}
}
fn transform_point(matrix: [[f32; 4]; 4], point: [f32; 3]) -> [f32; 3] {
[
matrix[0][0] * point[0]
+ matrix[1][0] * point[1]
+ matrix[2][0] * point[2]
+ matrix[3][0],
matrix[0][1] * point[0]
+ matrix[1][1] * point[1]
+ matrix[2][1] * point[2]
+ matrix[3][1],
matrix[0][2] * point[0]
+ matrix[1][2] * point[1]
+ matrix[2][2] * point[2]
+ matrix[3][2],
]
}
#[cfg(test)]
mod tests {
use super::*;
use crate::assets::MeshVertex;
#[test]
fn axis_uses_requested_world_length() {
let mut overlay = RenderDebugOverlay::default();
add_axis(
&mut overlay,
[2.0, 3.0, 4.0],
[1.0, 0.0, 0.0],
25.0,
[1.0; 4],
);
assert_eq!(overlay.lines.len(), 3);
assert_eq!(overlay.lines[0].end, [27.0, 3.0, 4.0]);
}
#[test]
fn mesh_radius_includes_object_scale() {
let mesh = MeshAsset {
vertices: vec![
MeshVertex {
position: [-1.0, -1.0, -1.0],
..MeshVertex::default()
},
MeshVertex {
position: [1.0, 1.0, 1.0],
..MeshVertex::default()
},
],
indices: Vec::new(),
};
let matrix = crate::Transform::default()
.with_scale(10.0, 10.0, 10.0)
.to_matrix();
let radius = mesh_world_radius_from_origin(&mesh, matrix).unwrap();
assert!((radius - 300.0_f32.sqrt()).abs() < 0.001);
}
#[test]
fn bound_box_has_twelve_edges() {
let mut overlay = RenderDebugOverlay::default();
add_bound_box(
&mut overlay,
[
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
],
[-1.0, -1.0, -1.0],
[1.0, 1.0, 1.0],
[1.0, 1.0, 0.0, 1.0],
);
assert_eq!(overlay.lines.len(), 12);
}
}