use crate::{
Error, Result,
vlm::{Mat4, Vec3},
};
use super::scene::{MeshData, MeshVertex};
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Aabb {
pub min: Vec3,
pub max: Vec3,
}
impl Aabb {
pub fn center(self) -> Vec3 {
Vec3 {
x: (self.min.x + self.max.x) * 0.5,
y: (self.min.y + self.max.y) * 0.5,
z: (self.min.z + self.max.z) * 0.5,
}
}
pub fn extent(self) -> Vec3 {
Vec3 {
x: self.max.x - self.min.x,
y: self.max.y - self.min.y,
z: self.max.z - self.min.z,
}
}
pub fn volume(self) -> f32 {
let e = self.extent();
e.x * e.y * e.z
}
pub fn contains(self, point: Vec3) -> bool {
point.x >= self.min.x
&& point.x <= self.max.x
&& point.y >= self.min.y
&& point.y <= self.max.y
&& point.z >= self.min.z
&& point.z <= self.max.z
}
}
pub fn create_quad(width: f32, height: f32) -> Result<MeshData> {
if !width.is_finite() || !height.is_finite() || width <= 0.0 || height <= 0.0 {
return Err(Error::invalid_argument(
"render::create_quad requires finite positive width and height",
));
}
let hx = width * 0.5;
let hy = height * 0.5;
let n = Vec3 {
x: 0.0,
y: 0.0,
z: 1.0,
};
let c = crate::vlm::Vec4 {
x: 1.0,
y: 1.0,
z: 1.0,
w: 1.0,
};
let mut mesh = MeshData {
vertices: vec![
MeshVertex {
position: Vec3 {
x: -hx,
y: hy,
z: 0.0,
},
normal: n,
uv: crate::vlm::Vec2 { x: 0.0, y: 0.0 },
color: c,
},
MeshVertex {
position: Vec3 {
x: hx,
y: hy,
z: 0.0,
},
normal: n,
uv: crate::vlm::Vec2 { x: 1.0, y: 0.0 },
color: c,
},
MeshVertex {
position: Vec3 {
x: hx,
y: -hy,
z: 0.0,
},
normal: n,
uv: crate::vlm::Vec2 { x: 1.0, y: 1.0 },
color: c,
},
MeshVertex {
position: Vec3 {
x: -hx,
y: -hy,
z: 0.0,
},
normal: n,
uv: crate::vlm::Vec2 { x: 0.0, y: 1.0 },
color: c,
},
],
indices: vec![0, 1, 2, 0, 2, 3],
bounds: Aabb::default(),
bounds_dirty: true,
};
compute_bounds(&mut mesh);
Ok(mesh)
}
pub fn create_fullscreen_quad() -> MeshData {
let n = Vec3 {
x: 0.0,
y: 0.0,
z: 1.0,
};
let c = crate::vlm::Vec4 {
x: 1.0,
y: 1.0,
z: 1.0,
w: 1.0,
};
let mut mesh = MeshData {
vertices: vec![
MeshVertex {
position: Vec3 {
x: -1.0,
y: 1.0,
z: 0.0,
},
normal: n,
uv: crate::vlm::Vec2 { x: 0.0, y: 0.0 },
color: c,
},
MeshVertex {
position: Vec3 {
x: 1.0,
y: 1.0,
z: 0.0,
},
normal: n,
uv: crate::vlm::Vec2 { x: 1.0, y: 0.0 },
color: c,
},
MeshVertex {
position: Vec3 {
x: 1.0,
y: -1.0,
z: 0.0,
},
normal: n,
uv: crate::vlm::Vec2 { x: 1.0, y: 1.0 },
color: c,
},
MeshVertex {
position: Vec3 {
x: -1.0,
y: -1.0,
z: 0.0,
},
normal: n,
uv: crate::vlm::Vec2 { x: 0.0, y: 1.0 },
color: c,
},
],
indices: vec![0, 1, 2, 0, 2, 3],
bounds: Aabb {
min: Vec3 {
x: -1.0,
y: -1.0,
z: 0.0,
},
max: Vec3 {
x: 1.0,
y: 1.0,
z: 0.0,
},
},
bounds_dirty: false,
};
compute_bounds(&mut mesh);
mesh
}
pub fn create_cube(size: f32) -> Result<MeshData> {
if !size.is_finite() || size <= 0.0 {
return Err(Error::invalid_argument(
"render::create_cube requires a finite positive size",
));
}
let h = size * 0.5;
let c = crate::vlm::Vec4 {
x: 1.0,
y: 1.0,
z: 1.0,
w: 1.0,
};
let face = |nx: f32, ny: f32, nz: f32, p0: [f32; 3], p1: [f32; 3], p2: [f32; 3], p3: [f32; 3]| {
let n = Vec3 {
x: nx,
y: ny,
z: nz,
};
[
MeshVertex {
position: Vec3 {
x: p0[0],
y: p0[1],
z: p0[2],
},
normal: n,
uv: crate::vlm::Vec2 { x: 0.0, y: 0.0 },
color: c,
},
MeshVertex {
position: Vec3 {
x: p1[0],
y: p1[1],
z: p1[2],
},
normal: n,
uv: crate::vlm::Vec2 { x: 1.0, y: 0.0 },
color: c,
},
MeshVertex {
position: Vec3 {
x: p2[0],
y: p2[1],
z: p2[2],
},
normal: n,
uv: crate::vlm::Vec2 { x: 1.0, y: 1.0 },
color: c,
},
MeshVertex {
position: Vec3 {
x: p3[0],
y: p3[1],
z: p3[2],
},
normal: n,
uv: crate::vlm::Vec2 { x: 0.0, y: 1.0 },
color: c,
},
]
};
let vertices: Vec<MeshVertex> = [
face(
0.0,
0.0,
1.0,
[-h, h, h],
[h, h, h],
[h, -h, h],
[-h, -h, h],
),
face(
0.0,
0.0,
-1.0,
[h, h, -h],
[-h, h, -h],
[-h, -h, -h],
[h, -h, -h],
),
face(
0.0,
1.0,
0.0,
[-h, h, -h],
[h, h, -h],
[h, h, h],
[-h, h, h],
),
face(
0.0,
-1.0,
0.0,
[-h, -h, h],
[h, -h, h],
[h, -h, -h],
[-h, -h, -h],
),
face(
1.0,
0.0,
0.0,
[h, h, h],
[h, h, -h],
[h, -h, -h],
[h, -h, h],
),
face(
-1.0,
0.0,
0.0,
[-h, h, -h],
[-h, h, h],
[-h, -h, h],
[-h, -h, -h],
),
]
.into_iter()
.flatten()
.collect();
let indices: Vec<u32> = (0u32..6)
.flat_map(|face| {
let b = face * 4;
[b, b + 1, b + 2, b, b + 2, b + 3]
})
.collect();
let mut mesh = MeshData {
vertices,
indices,
bounds: Aabb {
min: Vec3 {
x: -h,
y: -h,
z: -h,
},
max: Vec3 { x: h, y: h, z: h },
},
bounds_dirty: false,
};
compute_bounds(&mut mesh);
Ok(mesh)
}
pub fn compute_bounds(mesh: &mut MeshData) {
mesh.bounds_dirty = false;
if mesh.vertices.is_empty() {
mesh.bounds = Aabb::default();
return;
}
let first = mesh.vertices[0].position;
let mut mn = first;
let mut mx = first;
for v in &mesh.vertices {
let p = v.position;
if p.x < mn.x {
mn.x = p.x;
}
if p.y < mn.y {
mn.y = p.y;
}
if p.z < mn.z {
mn.z = p.z;
}
if p.x > mx.x {
mx.x = p.x;
}
if p.y > mx.y {
mx.y = p.y;
}
if p.z > mx.z {
mx.z = p.z;
}
}
mesh.bounds = Aabb { min: mn, max: mx };
}
pub fn transform(mesh: &mut MeshData, matrix: Mat4) -> Result<()> {
if !matrix.is_finite() {
return Err(Error::invalid_argument(
"render::transform requires a finite transformation matrix",
));
}
let normal_matrix = matrix
.try_normal_matrix(crate::vlm::INVERSE_TOLERANCE)
.ok_or_else(|| {
Error::invalid_argument("render::transform requires a non-singular transformation matrix")
})?;
let new_positions: Vec<Vec3> = mesh
.vertices
.iter()
.map(|v| matrix.transform_point(v.position))
.collect();
if new_positions.iter().any(|p| !p.is_finite()) {
return Err(Error::invalid_argument(
"render::transform produced non-finite vertex positions",
));
}
for (v, pos) in mesh.vertices.iter_mut().zip(new_positions) {
v.position = pos;
v.normal = v.normal * normal_matrix;
}
mesh.bounds_dirty = true;
Ok(())
}
pub fn translate(mesh: &mut MeshData, offset: Vec3) {
for v in &mut mesh.vertices {
v.position.x += offset.x;
v.position.y += offset.y;
v.position.z += offset.z;
}
if !mesh.bounds_dirty {
mesh.bounds.min.x += offset.x;
mesh.bounds.min.y += offset.y;
mesh.bounds.min.z += offset.z;
mesh.bounds.max.x += offset.x;
mesh.bounds.max.y += offset.y;
mesh.bounds.max.z += offset.z;
}
}
pub fn scale(mesh: &mut MeshData, s: Vec3) -> Result<()> {
if !s.is_finite() || s.x == 0.0 || s.y == 0.0 || s.z == 0.0 {
return Err(Error::invalid_argument(
"render::scale requires finite non-zero scale components",
));
}
let inv = Vec3 {
x: 1.0 / s.x,
y: 1.0 / s.y,
z: 1.0 / s.z,
};
for v in &mut mesh.vertices {
v.position.x *= s.x;
v.position.y *= s.y;
v.position.z *= s.z;
let nx = v.normal.x * inv.x;
let ny = v.normal.y * inv.y;
let nz = v.normal.z * inv.z;
let len = (nx * nx + ny * ny + nz * nz).sqrt();
if len > f32::EPSILON {
v.normal = Vec3 {
x: nx / len,
y: ny / len,
z: nz / len,
};
}
}
mesh.bounds_dirty = true;
Ok(())
}
pub fn flip_uvs_y(mesh: &mut MeshData) {
for v in &mut mesh.vertices {
v.uv.y = 1.0 - v.uv.y;
}
}