use alloc::{vec, vec::Vec};
use core::{
fmt::{Debug, Formatter},
iter::zip,
};
use crate::{
math::{Apply, Linear, Mat4x4, Point3, mat::RealToReal},
render::Model,
};
use super::{Normal3, Tri, Vertex3, tri, vertex};
#[derive(Clone)]
pub struct Mesh<Attrib, Basis = Model> {
pub faces: Vec<Tri<usize>>,
pub verts: Vec<Vertex3<Attrib, Basis>>,
}
#[derive(Clone)]
pub struct Builder<Attrib = (), Basis = Model> {
pub mesh: Mesh<Attrib, Basis>,
}
impl<A, B> Mesh<A, B> {
pub fn new<F, V>(faces: F, verts: V) -> Self
where
F: IntoIterator<Item = Tri<usize>>,
V: IntoIterator<Item = Vertex3<A, B>>,
{
let faces: Vec<_> = faces.into_iter().collect();
let verts: Vec<_> = verts.into_iter().collect();
for (i, Tri(vs)) in faces.iter().enumerate() {
assert!(
vs.iter().all(|&j| j < verts.len()),
"vertex index out of bounds at faces[{i}]: {vs:?}"
)
}
Self { faces, verts }
}
pub fn faces(&self) -> impl Iterator<Item = Tri<&Vertex3<A, B>>> {
self.faces
.iter()
.map(|Tri(vs)| Tri(vs.map(|i| &self.verts[i])))
}
pub fn merge(mut self, Self { faces, verts }: Self) -> Self {
let n = self.verts.len();
self.verts.extend(verts);
self.faces.extend(
faces
.into_iter()
.map(|Tri(ixs)| Tri(ixs.map(|i| n + i))),
);
self
}
}
impl<A> Mesh<A> {
pub fn builder() -> Builder<A> {
Builder::default()
}
pub fn into_builder(self) -> Builder<A> {
Builder { mesh: self }
}
}
impl<A> Builder<A> {
pub fn push_face(&mut self, a: usize, b: usize, c: usize) {
self.mesh.faces.push(tri(a, b, c));
}
pub fn push_faces<Fs>(&mut self, faces: Fs)
where
Fs: IntoIterator<Item = [usize; 3]>,
{
self.mesh.faces.extend(faces.into_iter().map(Tri));
}
pub fn push_vert(&mut self, pos: Point3, attrib: A) {
self.mesh.verts.push(vertex(pos.to(), attrib));
}
pub fn push_verts<Vs>(&mut self, verts: Vs)
where
Vs: IntoIterator<Item = (Point3, A)>,
{
let vs = verts.into_iter().map(|(p, a)| vertex(p.to(), a));
self.mesh.verts.extend(vs);
}
pub fn build(self) -> Mesh<A> {
Mesh::new(self.mesh.faces, self.mesh.verts)
}
}
impl<A> Builder<A> {
pub fn transform(self, tf: &Mat4x4<RealToReal<3, Model, Model>>) -> Self {
self.warp(|v| vertex(tf.apply(&v.pos), v.attrib))
}
pub fn warp(mut self, f: impl FnMut(Vertex3<A>) -> Vertex3<A>) -> Self {
self.mesh.verts = self.mesh.verts.into_iter().map(f).collect();
self
}
pub fn with_vertex_normals(self) -> Builder<Normal3> {
let Mesh { verts, faces } = self.mesh;
let face_normals = faces.iter().map(|Tri(vs)| {
let [a, b, c] = vs.map(|i| verts[i].pos);
(b - a).cross(&(c - a)).to()
});
let mut verts: Vec<_> = verts
.iter()
.map(|v| vertex(v.pos, Normal3::zero()))
.collect();
for (&Tri(vs), n) in zip(&faces, face_normals) {
for i in vs {
verts[i].attrib += n;
}
}
for v in &mut verts {
v.attrib = v.attrib.normalize();
}
Mesh::new(faces, verts).into_builder()
}
}
impl<A: Debug, S: Debug + Default> Debug for Mesh<A, S> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Mesh")
.field("faces", &self.faces)
.field("verts", &self.verts)
.finish()
}
}
impl<A: Debug, S: Debug + Default> Debug for Builder<A, S> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Builder")
.field("faces", &self.mesh.faces)
.field("verts", &self.mesh.verts)
.finish()
}
}
impl<A, S> Default for Mesh<A, S> {
fn default() -> Self {
Self { faces: vec![], verts: vec![] }
}
}
impl<A> Default for Builder<A> {
fn default() -> Self {
Self { mesh: Mesh::default() }
}
}
#[cfg(test)]
mod tests {
use core::f32::consts::FRAC_1_SQRT_2;
use crate::{
geom::vertex,
math::{pt3, splat, vec3},
};
use super::*;
#[test]
#[should_panic]
fn mesh_new_panics_if_vertex_index_oob() {
let _: Mesh<()> = Mesh::new(
[tri(0, 1, 2), tri(1, 2, 3)],
[
vertex(pt3(0.0, 0.0, 0.0), ()),
vertex(pt3(1.0, 1.0, 1.0), ()),
vertex(pt3(2.0, 2.0, 2.0), ()),
],
);
}
#[test]
#[should_panic]
fn mesh_builder_panics_if_vertex_index_oob() {
let mut b = Mesh::builder();
b.push_faces([[0, 1, 2], [1, 2, 3]]);
b.push_verts([
(pt3(0.0, 0.0, 0.0), ()),
(pt3(1.0, 1.0, 1.0), ()),
(pt3(2.0, 2.0, 2.0), ()),
]);
_ = b.build();
}
#[test]
fn vertex_normal_generation() {
let mut b = Mesh::builder();
b.push_faces([[0, 2, 1], [0, 1, 3], [0, 3, 2]]);
b.push_verts([
(pt3(0.0, 0.0, 0.0), ()),
(pt3(1.0, 0.0, 0.0), ()),
(pt3(0.0, 1.0, 0.0), ()),
(pt3(0.0, 0.0, 1.0), ()),
]);
let b = b.with_vertex_normals();
const SQRT_3: f32 = 1.7320508076;
let expected = [
splat(-1.0 / SQRT_3),
vec3(0.0, -FRAC_1_SQRT_2, -FRAC_1_SQRT_2),
vec3(-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2),
vec3(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.0),
];
for i in 0..4 {
crate::assert_approx_eq!(b.mesh.verts[i].attrib, expected[i]);
}
}
}