retrofire_core/geom/mesh.rs
1//! Triangle meshes.
2
3use alloc::{vec, vec::Vec};
4use core::{
5 fmt::{Debug, Formatter},
6 iter::zip,
7};
8
9use crate::{
10 math::{Apply, Linear, Mat4x4, Point3, mat::RealToReal},
11 render::Model,
12};
13
14use super::{Normal3, Tri, Vertex3, tri, vertex};
15
16/// A triangle mesh.
17///
18/// An object made of flat polygonal faces that typically form a contiguous
19/// surface without holes or boundaries, so that every face shares each of its
20/// edges with another face. For instance, a cube can be represented by a mesh
21/// with 8 vertices and 12 faces. By using many faces, complex curved shapes
22/// can be approximated.
23#[derive(Clone)]
24pub struct Mesh<Attrib, Basis = Model> {
25 /// The faces of the mesh, with each face a triplet of indices
26 /// to the `verts` vector. Several faces can share a vertex.
27 pub faces: Vec<Tri<usize>>,
28 /// The vertices of the mesh.
29 pub verts: Vec<Vertex3<Attrib, Basis>>,
30}
31
32/// A builder type for creating meshes.
33#[derive(Clone)]
34pub struct Builder<Attrib = (), Basis = Model> {
35 pub mesh: Mesh<Attrib, Basis>,
36}
37
38//
39// Inherent impls
40//
41
42impl<A, B> Mesh<A, B> {
43 /// Creates a new triangle mesh with the given faces and vertices.
44 ///
45 /// Each face in `faces` is a triplet of indices, referring to
46 /// the vertices in `verts` that define that face.
47 ///
48 /// # Examples
49 /// ```
50 /// use retrofire_core::geom::{Mesh, tri, vertex};
51 /// use retrofire_core::math::pt3;
52 ///
53 /// let verts = [
54 /// pt3(0.0, 0.0, 0.0),
55 /// pt3(1.0, 0.0, 0.0),
56 /// pt3(0.0, 1.0, 0.0),
57 /// pt3(0.0, 0.0, 1.0)
58 /// ]
59 /// .map(|v| vertex(v, ()));
60 ///
61 /// let faces = [
62 /// // Indices point to the verts array
63 /// tri(0, 1, 2),
64 /// tri(0, 1, 3),
65 /// tri(0, 2, 3),
66 /// tri(1, 2, 3)
67 /// ];
68 ///
69 /// // Create a mesh with a tetrahedral shape
70 /// let tetra: Mesh<()> = Mesh::new(faces, verts);
71 /// ```
72 /// # Panics
73 /// If any of the vertex indices in `faces` ≥ `verts.len()`.
74 pub fn new<F, V>(faces: F, verts: V) -> Self
75 where
76 F: IntoIterator<Item = Tri<usize>>,
77 V: IntoIterator<Item = Vertex3<A, B>>,
78 {
79 let faces: Vec<_> = faces.into_iter().collect();
80 let verts: Vec<_> = verts.into_iter().collect();
81
82 for (i, Tri(vs)) in faces.iter().enumerate() {
83 assert!(
84 vs.iter().all(|&j| j < verts.len()),
85 "vertex index out of bounds at faces[{i}]: {vs:?}"
86 )
87 }
88 Self { faces, verts }
89 }
90
91 /// Returns an iterator over the faces of `self`, mapping the vertex indices
92 /// to references to the corresponding vertices.
93 pub fn faces(&self) -> impl Iterator<Item = Tri<&Vertex3<A, B>>> {
94 self.faces
95 .iter()
96 .map(|Tri(vs)| Tri(vs.map(|i| &self.verts[i])))
97 }
98
99 /// Returns a mesh with the faces and vertices of both `self` and `other`.
100 pub fn merge(mut self, Self { faces, verts }: Self) -> Self {
101 let n = self.verts.len();
102 self.verts.extend(verts);
103 self.faces.extend(
104 faces
105 .into_iter()
106 .map(|Tri(ixs)| Tri(ixs.map(|i| n + i))),
107 );
108 self
109 }
110}
111
112impl<A> Mesh<A> {
113 /// Returns a new mesh builder.
114 pub fn builder() -> Builder<A> {
115 Builder::default()
116 }
117
118 /// Consumes `self` and returns a mesh builder with the faces and vertices
119 /// of `self`.
120 pub fn into_builder(self) -> Builder<A> {
121 Builder { mesh: self }
122 }
123}
124
125impl<A> Builder<A> {
126 /// Appends a face with the given vertex indices.
127 ///
128 /// Invalid indices (referring to vertices not yet added) are permitted,
129 /// as long as all indices are valid when the [`build`][Builder::build]
130 /// method is called.
131 pub fn push_face(&mut self, a: usize, b: usize, c: usize) {
132 self.mesh.faces.push(tri(a, b, c));
133 }
134
135 /// Appends all the faces yielded by the given iterator.
136 ///
137 /// The faces may include invalid vertex indices (referring to vertices
138 /// not yet added) are permitted, as long as all indices are valid when
139 /// the [`build`][Builder::build] method is called.
140 pub fn push_faces<Fs>(&mut self, faces: Fs)
141 where
142 Fs: IntoIterator<Item = [usize; 3]>,
143 {
144 self.mesh.faces.extend(faces.into_iter().map(Tri));
145 }
146
147 /// Appends a vertex with the given position and attribute.
148 pub fn push_vert(&mut self, pos: Point3, attrib: A) {
149 self.mesh.verts.push(vertex(pos.to(), attrib));
150 }
151
152 /// Appends all the vertices yielded by the given iterator.
153 pub fn push_verts<Vs>(&mut self, verts: Vs)
154 where
155 Vs: IntoIterator<Item = (Point3, A)>,
156 {
157 let vs = verts.into_iter().map(|(p, a)| vertex(p.to(), a));
158 self.mesh.verts.extend(vs);
159 }
160
161 /// Returns the finished mesh containing all the added faces and vertices.
162 ///
163 /// # Panics
164 /// If any of the vertex indices in `faces` ≥ `verts.len()`.
165 pub fn build(self) -> Mesh<A> {
166 // Sanity checks done by new()
167 Mesh::new(self.mesh.faces, self.mesh.verts)
168 }
169}
170
171impl<A> Builder<A> {
172 /// Applies the given transform to the position of each vertex.
173 ///
174 /// This is an eager operation, that is, only vertices *currently*
175 /// added to the builder are transformed.
176 pub fn transform(self, tf: &Mat4x4<RealToReal<3, Model, Model>>) -> Self {
177 self.warp(|v| vertex(tf.apply(&v.pos), v.attrib))
178 }
179
180 /// Applies an arbitrary mapping to each vertex.
181 ///
182 /// This method can be used for various nonlinear transformations such as
183 /// twisting or dilation. This is an eager operation, that is, only vertices
184 /// *currently* added to the builder are transformed.
185 pub fn warp(mut self, f: impl FnMut(Vertex3<A>) -> Vertex3<A>) -> Self {
186 self.mesh.verts = self.mesh.verts.into_iter().map(f).collect();
187 self
188 }
189
190 /// Computes a vertex normal for each vertex as an area-weighted average
191 /// of normals of the faces adjacent to it.
192 ///
193 /// The algorithm is as follows:
194 /// 1. Initialize the normal of each vertex to **0**
195 /// 1. For each face:
196 /// 1. Take the cross product of two of the face's edge vectors
197 /// 2. Add the result to the normal of each of the face's vertices.
198 /// 3. Normalize each vertex normal to unit length.
199 ///
200 /// This is an eager operation, that is, only vertices *currently* added
201 /// to the builder are transformed. The attribute type of the result is
202 /// `Normal3`; the vertex type it accepts is changed accordingly.
203 pub fn with_vertex_normals(self) -> Builder<Normal3> {
204 let Mesh { verts, faces } = self.mesh;
205
206 // Compute weighted face normals...
207 let face_normals = faces.iter().map(|Tri(vs)| {
208 // TODO If n-gonal faces are supported some day, the cross
209 // product is not proportional to area anymore
210 let [a, b, c] = vs.map(|i| verts[i].pos);
211 (b - a).cross(&(c - a)).to()
212 });
213 // ...initialize vertex normals to zero...
214 let mut verts: Vec<_> = verts
215 .iter()
216 .map(|v| vertex(v.pos, Normal3::zero()))
217 .collect();
218 // ...accumulate normals...
219 for (&Tri(vs), n) in zip(&faces, face_normals) {
220 for i in vs {
221 verts[i].attrib += n;
222 }
223 }
224 // ...and normalize to unit length.
225 for v in &mut verts {
226 v.attrib = v.attrib.normalize();
227 }
228
229 Mesh::new(faces, verts).into_builder()
230 }
231}
232
233//
234// Foreign trait impls
235//
236
237impl<A: Debug, S: Debug + Default> Debug for Mesh<A, S> {
238 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
239 f.debug_struct("Mesh")
240 .field("faces", &self.faces)
241 .field("verts", &self.verts)
242 .finish()
243 }
244}
245
246impl<A: Debug, S: Debug + Default> Debug for Builder<A, S> {
247 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
248 f.debug_struct("Builder")
249 .field("faces", &self.mesh.faces)
250 .field("verts", &self.mesh.verts)
251 .finish()
252 }
253}
254
255impl<A, S> Default for Mesh<A, S> {
256 /// Returns an empty mesh.
257 fn default() -> Self {
258 Self { faces: vec![], verts: vec![] }
259 }
260}
261
262impl<A> Default for Builder<A> {
263 /// Returns an empty builder.
264 fn default() -> Self {
265 Self { mesh: Mesh::default() }
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use core::f32::consts::FRAC_1_SQRT_2;
272
273 use crate::{
274 geom::vertex,
275 math::{pt3, splat, vec3},
276 };
277
278 use super::*;
279
280 #[test]
281 #[should_panic]
282 fn mesh_new_panics_if_vertex_index_oob() {
283 let _: Mesh<()> = Mesh::new(
284 [tri(0, 1, 2), tri(1, 2, 3)],
285 [
286 vertex(pt3(0.0, 0.0, 0.0), ()),
287 vertex(pt3(1.0, 1.0, 1.0), ()),
288 vertex(pt3(2.0, 2.0, 2.0), ()),
289 ],
290 );
291 }
292
293 #[test]
294 #[should_panic]
295 fn mesh_builder_panics_if_vertex_index_oob() {
296 let mut b = Mesh::builder();
297 b.push_faces([[0, 1, 2], [1, 2, 3]]);
298 b.push_verts([
299 (pt3(0.0, 0.0, 0.0), ()),
300 (pt3(1.0, 1.0, 1.0), ()),
301 (pt3(2.0, 2.0, 2.0), ()),
302 ]);
303 _ = b.build();
304 }
305
306 #[test]
307 fn vertex_normal_generation() {
308 // TODO Doesn't test weighting by area
309
310 let mut b = Mesh::builder();
311 b.push_faces([[0, 2, 1], [0, 1, 3], [0, 3, 2]]);
312 b.push_verts([
313 (pt3(0.0, 0.0, 0.0), ()),
314 (pt3(1.0, 0.0, 0.0), ()),
315 (pt3(0.0, 1.0, 0.0), ()),
316 (pt3(0.0, 0.0, 1.0), ()),
317 ]);
318 let b = b.with_vertex_normals();
319
320 const SQRT_3: f32 = 1.7320508076;
321
322 let expected = [
323 splat(-1.0 / SQRT_3),
324 vec3(0.0, -FRAC_1_SQRT_2, -FRAC_1_SQRT_2),
325 vec3(-FRAC_1_SQRT_2, 0.0, -FRAC_1_SQRT_2),
326 vec3(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.0),
327 ];
328
329 for i in 0..4 {
330 crate::assert_approx_eq!(b.mesh.verts[i].attrib, expected[i]);
331 }
332 }
333}