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}