Skip to main content

threecrate_core/
mesh.rs

1//! Mesh data structures and functionality
2
3use crate::point::*;
4// use nalgebra::{Point3, Vector3};
5use serde::{Deserialize, Serialize};
6
7/// A triangle mesh with vertices and faces
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct TriangleMesh {
10    pub vertices: Vec<Point3f>,
11    pub faces: Vec<[usize; 3]>,
12    pub normals: Option<Vec<Vector3f>>,
13    pub colors: Option<Vec<[u8; 3]>>,
14}
15
16/// A mesh with colored vertices
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct ColoredTriangleMesh {
19    pub vertices: Vec<ColoredPoint3f>,
20    pub faces: Vec<[usize; 3]>,
21    pub normals: Option<Vec<Vector3f>>,
22}
23
24impl TriangleMesh {
25    /// Create a new empty mesh
26    pub fn new() -> Self {
27        Self {
28            vertices: Vec::new(),
29            faces: Vec::new(),
30            normals: None,
31            colors: None,
32        }
33    }
34
35    /// Create a mesh from vertices and faces
36    pub fn from_vertices_and_faces(vertices: Vec<Point3f>, faces: Vec<[usize; 3]>) -> Self {
37        Self {
38            vertices,
39            faces,
40            normals: None,
41            colors: None,
42        }
43    }
44
45    /// Get the number of vertices
46    pub fn vertex_count(&self) -> usize {
47        self.vertices.len()
48    }
49
50    /// Get the number of faces
51    pub fn face_count(&self) -> usize {
52        self.faces.len()
53    }
54
55    /// Check if the mesh is empty
56    pub fn is_empty(&self) -> bool {
57        self.vertices.is_empty() || self.faces.is_empty()
58    }
59
60    /// Check that every face index points at an existing vertex. Meshes from
61    /// files or user arrays can hold any number; algorithms index without checks.
62    pub fn validate_faces(&self) -> crate::Result<()> {
63        let n = self.vertices.len();
64        match self.faces.iter().position(|f| f.iter().any(|&i| i >= n)) {
65            None => Ok(()),
66            Some(face) => Err(crate::Error::InvalidData(format!(
67                "face {face} uses vertex index {:?}, but the mesh has only {n} vertices",
68                self.faces[face]
69            ))),
70        }
71    }
72
73    /// Add a vertex to the mesh
74    pub fn add_vertex(&mut self, vertex: Point3f) -> usize {
75        let index = self.vertices.len();
76        self.vertices.push(vertex);
77        index
78    }
79
80    /// Add a face to the mesh
81    pub fn add_face(&mut self, face: [usize; 3]) {
82        self.faces.push(face);
83    }
84
85    /// Calculate face normals
86    pub fn calculate_face_normals(&self) -> Vec<Vector3f> {
87        self.faces
88            .iter()
89            .map(|face| {
90                let v0 = self.vertices[face[0]];
91                let v1 = self.vertices[face[1]];
92                let v2 = self.vertices[face[2]];
93
94                let edge1 = v1 - v0;
95                let edge2 = v2 - v0;
96
97                edge1.cross(&edge2).normalize()
98            })
99            .collect()
100    }
101
102    /// Set vertex normals
103    pub fn set_normals(&mut self, normals: Vec<Vector3f>) {
104        if normals.len() == self.vertices.len() {
105            self.normals = Some(normals);
106        }
107    }
108
109    /// Set vertex colors
110    pub fn set_colors(&mut self, colors: Vec<[u8; 3]>) {
111        if colors.len() == self.vertices.len() {
112            self.colors = Some(colors);
113        }
114    }
115
116    /// Clear the mesh
117    pub fn clear(&mut self) {
118        self.vertices.clear();
119        self.faces.clear();
120        self.normals = None;
121        self.colors = None;
122    }
123}
124
125impl Default for TriangleMesh {
126    fn default() -> Self {
127        Self::new()
128    }
129}
130
131impl ColoredTriangleMesh {
132    /// Create a new empty colored mesh
133    pub fn new() -> Self {
134        Self {
135            vertices: Vec::new(),
136            faces: Vec::new(),
137            normals: None,
138        }
139    }
140
141    /// Get the number of vertices
142    pub fn vertex_count(&self) -> usize {
143        self.vertices.len()
144    }
145
146    /// Get the number of faces
147    pub fn face_count(&self) -> usize {
148        self.faces.len()
149    }
150
151    /// Check if the mesh is empty
152    pub fn is_empty(&self) -> bool {
153        self.vertices.is_empty() || self.faces.is_empty()
154    }
155}
156
157impl Default for ColoredTriangleMesh {
158    fn default() -> Self {
159        Self::new()
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166
167    #[test]
168    fn validate_faces_rejects_out_of_range_indices() {
169        let vertices = vec![Point3f::origin(); 3];
170        assert!(
171            TriangleMesh::from_vertices_and_faces(vertices.clone(), vec![[0, 1, 2]])
172                .validate_faces()
173                .is_ok()
174        );
175        let err = TriangleMesh::from_vertices_and_faces(vertices, vec![[0, 1, 2], [0, 1, 99]])
176            .validate_faces()
177            .unwrap_err();
178        assert!(err.to_string().contains("face 1"), "{err}");
179    }
180}