use glam::Vec3;
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Vertex(pub u16);
impl From<usize> for Vertex {
fn from(v: usize) -> Self {
Self(v.try_into().expect("Too many vertices"))
}
}
#[derive(Clone, Copy, Debug)]
pub struct Face {
vtx: [usize; 3],
surface: u16,
}
#[derive(Default)]
pub struct MeshBuilder {
pos: Vec<Vec3>,
faces: Vec<Face>,
}
pub struct Mesh {
pos: Vec<Vec3>,
norm: Vec<Vec3>,
indices: Vec<Vertex>,
}
impl Face {
pub fn new(vtx: [usize; 3], surface: u16) -> Self {
debug_assert_ne!(vtx[0], vtx[1]);
debug_assert_ne!(vtx[1], vtx[2]);
debug_assert_ne!(vtx[2], vtx[0]);
Self { vtx, surface }
}
fn vertex_surface(&self, idx: usize) -> Option<u16> {
self.vtx.contains(&idx).then_some(self.surface)
}
fn split_vertex(&mut self, idx: usize, i: usize) {
if self.vtx[0] == idx {
self.vtx[0] = i;
} else if self.vtx[1] == idx {
self.vtx[1] = i;
} else if self.vtx[2] == idx {
self.vtx[2] = i;
}
}
}
impl MeshBuilder {
fn with_capacity(n_faces: usize) -> Self {
let pos = Vec::with_capacity(n_faces * 3);
let faces = Vec::with_capacity(n_faces * 3);
MeshBuilder { pos, faces }
}
pub fn vertex(&self, idx: usize) -> Vec3 {
self.pos[idx]
}
pub fn push_vtx(&mut self, pos: Vec3) -> usize {
let idx = self.pos.len();
self.pos.push(pos);
idx
}
pub fn push_face(&mut self, face: Face) {
let idx = self.pos.len();
if face.vtx[0] >= idx || face.vtx[1] >= idx || face.vtx[2] >= idx {
panic!("Invalid vertex");
}
self.faces.push(face);
}
pub fn build(self) -> Mesh {
Mesh::new(self.split_vertices())
}
fn split_vertices(mut self) -> Self {
let vertices = self.pos.len();
for idx in 0..vertices {
while self.vertex_needs_split(idx) {
self.split_vertex(idx);
}
}
self
}
fn vertex_needs_split(&self, idx: usize) -> bool {
let mut surface = None;
for face in &self.faces {
let surf = face.vertex_surface(idx);
if let (Some(s), Some(sf)) = (surf, surface) {
if s != sf {
return true;
}
}
surface = surf;
}
false
}
fn split_vertex(&mut self, idx: usize) {
let mut surfaces = Vec::with_capacity(8);
for face in &self.faces {
if let Some(surf) = face.vertex_surface(idx) {
if surfaces.is_empty() {
surfaces.push((surf, idx));
} else if !surfaces.iter().any(|(s, _i)| surf == *s) {
surfaces.push((surf, 0));
}
}
}
let pos = self.pos[idx];
for surface in &mut surfaces {
if surface.1 == 0 {
surface.1 = self.push_vtx(pos);
}
}
for face in &mut self.faces {
if let Some(surf) = face.vertex_surface(idx) {
if let Some(i) =
surfaces.iter().find_map(|(s, i)| (surf == *s).then_some(i))
{
if *i != idx {
face.split_vertex(idx, *i);
}
}
}
}
}
fn build_normals(&self) -> Vec<Vec3> {
let vertices = self.pos.len();
let mut norm = vec![Vec3::default(); vertices];
for face in &self.faces {
let vtx = [face.vtx[0], face.vtx[1], face.vtx[2]];
let pos = [self.pos[vtx[0]], self.pos[vtx[1]], self.pos[vtx[2]]];
let trin = (pos[0] - pos[1]).cross(pos[0] - pos[2]).normalize();
let a0 = (pos[1] - pos[0]).angle_between(pos[2] - pos[0]);
norm[vtx[0]] += trin * a0;
let a1 = (pos[2] - pos[1]).angle_between(pos[0] - pos[1]);
norm[vtx[1]] += trin * a1;
let a2 = (pos[0] - pos[2]).angle_between(pos[1] - pos[2]);
norm[vtx[2]] += trin * a2;
}
norm.iter().map(|n| n.normalize()).collect()
}
fn build_indices(&self) -> Vec<Vertex> {
let mut indices = Vec::with_capacity(self.faces.len() * 3);
for face in &self.faces {
indices.push(face.vtx[0].into());
indices.push(face.vtx[1].into());
indices.push(face.vtx[2].into());
}
indices
}
}
impl Mesh {
pub fn builder() -> MeshBuilder {
MeshBuilder::with_capacity(1024)
}
fn new(builder: MeshBuilder) -> Self {
let norm = builder.build_normals();
let indices = builder.build_indices();
let pos = builder.pos;
Mesh { pos, norm, indices }
}
pub fn positions(&self) -> &[Vec3] {
&self.pos[..]
}
pub fn normals(&self) -> &[Vec3] {
&self.norm[..]
}
pub fn indices(&self) -> &[Vertex] {
&self.indices[..]
}
pub fn pos_min(&self) -> Vec3 {
self.positions()
.iter()
.copied()
.reduce(|min, v| v.min(min))
.unwrap()
}
pub fn pos_max(&self) -> Vec3 {
self.positions()
.iter()
.copied()
.reduce(|max, v| v.max(max))
.unwrap()
}
}