draco_core/mesh.rs
1use crate::geometry_indices::{AttributeValueIndex, FaceIndex, PointIndex, VertexIndex};
2use crate::point_cloud::PointCloud;
3use crate::status::{DracoError, Status};
4use std::collections::HashMap;
5use std::ops::{Deref, DerefMut};
6
7/// Triangle face represented by three point indices.
8pub type Face = [PointIndex; 3];
9
10/// Triangle mesh geometry decoded from, or prepared for, a Draco bitstream.
11///
12/// A mesh owns triangle topology and dereferences to its underlying
13/// [`PointCloud`], where attributes and metadata are stored.
14#[derive(Debug, Default, Clone)]
15pub struct Mesh {
16 point_cloud: PointCloud,
17 faces: Vec<Face>,
18}
19
20impl Mesh {
21 /// Creates an empty mesh with no faces, points, attributes, or metadata.
22 pub fn new() -> Self {
23 Self::default()
24 }
25
26 /// Drops every face and everything the underlying point cloud holds,
27 /// keeping the allocated capacity of both lists.
28 ///
29 /// What a decode does to the mesh it is given, so that decoding into one
30 /// that already holds geometry replaces it rather than adding to it.
31 pub fn clear(&mut self) {
32 self.point_cloud.clear();
33 self.faces.clear();
34 }
35
36 /// Appends one triangle face.
37 pub fn add_face(&mut self, face: Face) {
38 self.faces.push(face);
39 }
40
41 /// Sets a face, growing the face list with zeroed faces when needed.
42 pub fn set_face(&mut self, face_id: FaceIndex, face: Face) {
43 if face_id.0 as usize >= self.faces.len() {
44 self.faces
45 .resize(face_id.0 as usize + 1, [PointIndex(0); 3]);
46 }
47 self.faces[face_id.0 as usize] = face;
48 }
49
50 /// Bulk-set all faces from a flat u32 index array (3 indices per face).
51 /// Assumes `set_num_faces` has already been called with the right count.
52 #[inline]
53 /// Fills every face from a corner table's corner-to-vertex map.
54 ///
55 /// The map lays the three corners of face `f` at `3f..3f + 3`, in the
56 /// order a face stores them, so an edgebreaker decode without attribute
57 /// seams -- where a corner-table vertex index *is* a point index -- is a
58 /// straight copy. Reading it back through `vertex`/`vertex_after`/
59 /// `vertex_before` instead costs three bounds-checked `Option` lookups
60 /// and two modular corner computations per face for indices already
61 /// known to be consecutive: 51 instructions per face against upstream's
62 /// 23, on a table whose bounds the caller's consistency scan has just
63 /// proved.
64 pub fn set_faces_from_corner_vertices(&mut self, corner_to_vertex_map: &[VertexIndex]) {
65 let (corners_per_face, _) = corner_to_vertex_map.as_chunks::<3>();
66 // Matches `set_face`, which grows rather than refusing a face past
67 // the end; the edgebreaker caller has already sized the mesh to the
68 // table, so this is a fallback and not the path taken.
69 if self.faces.len() < corners_per_face.len() {
70 self.faces
71 .resize(corners_per_face.len(), [PointIndex(0); 3]);
72 }
73 for (face, corners) in self.faces.iter_mut().zip(corners_per_face) {
74 *face = [
75 PointIndex(corners[0].0),
76 PointIndex(corners[1].0),
77 PointIndex(corners[2].0),
78 ];
79 }
80 }
81
82 pub fn set_faces_from_flat_indices(&mut self, indices: &[u32]) {
83 debug_assert_eq!(indices.len(), self.faces.len() * 3);
84 for (i, face) in self.faces.iter_mut().enumerate() {
85 let base = i * 3;
86 *face = [
87 PointIndex(indices[base]),
88 PointIndex(indices[base + 1]),
89 PointIndex(indices[base + 2]),
90 ];
91 }
92 }
93
94 /// Bulk-set all faces from tightly packed u8 indices.
95 /// Assumes `set_num_faces` has already been called with the right count.
96 #[inline]
97 pub fn set_faces_from_u8_indices(&mut self, bytes: &[u8]) {
98 debug_assert_eq!(bytes.len(), self.faces.len() * 3);
99 for (face, chunk) in self.faces.iter_mut().zip(bytes.as_chunks::<3>().0) {
100 *face = [
101 PointIndex(chunk[0] as u32),
102 PointIndex(chunk[1] as u32),
103 PointIndex(chunk[2] as u32),
104 ];
105 }
106 }
107
108 /// Bulk-set all faces from tightly packed little-endian u16 indices.
109 /// Assumes `set_num_faces` has already been called with the right count.
110 #[inline]
111 pub fn set_faces_from_le_u16_indices(&mut self, bytes: &[u8]) {
112 debug_assert_eq!(bytes.len(), self.faces.len() * 3 * 2);
113 for (face, chunk) in self.faces.iter_mut().zip(bytes.as_chunks::<6>().0) {
114 *face = [
115 PointIndex(u16::from_le_bytes([chunk[0], chunk[1]]) as u32),
116 PointIndex(u16::from_le_bytes([chunk[2], chunk[3]]) as u32),
117 PointIndex(u16::from_le_bytes([chunk[4], chunk[5]]) as u32),
118 ];
119 }
120 }
121
122 /// Bulk-set all faces from tightly packed little-endian u32 indices.
123 /// Assumes `set_num_faces` has already been called with the right count.
124 #[inline]
125 pub fn set_faces_from_le_u32_indices(&mut self, bytes: &[u8]) {
126 debug_assert_eq!(bytes.len(), self.faces.len() * 3 * 4);
127 for (face, chunk) in self.faces.iter_mut().zip(bytes.as_chunks::<12>().0) {
128 *face = [
129 PointIndex(u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]])),
130 PointIndex(u32::from_le_bytes([chunk[4], chunk[5], chunk[6], chunk[7]])),
131 PointIndex(u32::from_le_bytes([
132 chunk[8], chunk[9], chunk[10], chunk[11],
133 ])),
134 ];
135 }
136 }
137
138 /// Sets one face from raw u32 point ids.
139 #[inline]
140 pub fn set_face_from_indices(&mut self, face_id: usize, indices: [u32; 3]) {
141 self.faces[face_id] = [
142 PointIndex(indices[0]),
143 PointIndex(indices[1]),
144 PointIndex(indices[2]),
145 ];
146 }
147
148 /// Returns the point indices for a face.
149 pub fn face(&self, face_id: FaceIndex) -> Face {
150 self.faces[face_id.0 as usize]
151 }
152
153 /// Every face's point indices, in face order.
154 ///
155 /// For a caller that walks all of them: `as_flattened()` on the result is
156 /// the mesh's corners in the corner table's own order, which lets a walk
157 /// over both zip two slices instead of deriving a corner index from a face
158 /// index and re-proving the bound at each of them.
159 pub fn faces(&self) -> &[Face] {
160 &self.faces
161 }
162
163 /// Returns the number of triangle faces.
164 pub fn num_faces(&self) -> usize {
165 self.faces.len()
166 }
167
168 /// Resizes the face list, filling new faces with point index zero.
169 pub fn set_num_faces(&mut self, num_faces: usize) {
170 self.faces.resize(num_faces, [PointIndex(0); 3]);
171 }
172
173 /// Fallibly resizes the face list.
174 pub fn try_set_num_faces(&mut self, num_faces: usize) -> Status {
175 if num_faces > self.faces.len() {
176 self.faces
177 .try_reserve_exact(num_faces - self.faces.len())
178 .map_err(|_| DracoError::general("Failed to allocate mesh faces".to_string()))?;
179 }
180 self.faces.resize(num_faces, [PointIndex(0); 3]);
181 Ok(())
182 }
183
184 /// Merges points whose attribute values all coincide, and rewrites the
185 /// faces that named them.
186 ///
187 /// Port of upstream's `Mesh::ApplyPointIdDeduplication` path: the point
188 /// cloud merges the points, then the faces follow the same map. Pair it
189 /// with [`deduplicate_attribute_values`](crate::PointCloud::deduplicate_attribute_values),
190 /// which has to run first -- two vertices carrying equal bytes hold
191 /// distinct value indices until it merges them, so nothing here would see
192 /// them as one point.
193 pub fn deduplicate_point_ids(&mut self) {
194 self.deduplicate_point_ids_returning_map();
195 }
196
197 /// [`deduplicate_point_ids`](Self::deduplicate_point_ids), additionally
198 /// handing back the old-point-to-new-point map -- identity when nothing
199 /// merged -- for a caller that has to carry data addressed by the
200 /// original point (an FBX corner's skin weight or morph delta) onto the
201 /// point that now stands in for it.
202 pub fn deduplicate_point_ids_returning_map(&mut self) -> Vec<u32> {
203 let original_num_points = self.num_points() as u32;
204 let Some(index_map) = self.point_cloud.deduplicate_point_ids_returning_map() else {
205 return (0..original_num_points).collect();
206 };
207 for face in &mut self.faces {
208 for corner in face.iter_mut() {
209 // A corner past the point count keeps its value. Such a face
210 // is not this function's to reject -- the encoder refuses it
211 // where the refusal can be reported -- and there is no new id
212 // to map it onto.
213 if let Some(new) = index_map.get(corner.0 as usize) {
214 *corner = PointIndex(*new);
215 }
216 }
217 }
218 index_map
219 }
220
221 /// Drops points no face names, and then the attribute values left with no
222 /// point, keeping everything else in the order it was in.
223 ///
224 /// Nothing downstream keeps such a point: both this encoder and upstream's
225 /// write the geometry the connectivity reaches, so an unreferenced vertex
226 /// never reaches a decoder either way. What it does reach is the
227 /// quantization range, which is computed over the values an attribute
228 /// holds -- so a stray vertex far from the mesh spends bits on empty space
229 /// and every coordinate that survives comes back less precisely. Measured
230 /// on a unit triangle with a fourth vertex at `1000, 1000, 1000`: the
231 /// encoded size does not move and `1.0` returns as `1.007095`.
232 ///
233 /// Upstream keeps them, which is why `COMPATIBILITY.md` carries this. Its
234 /// readers size a position attribute from the vertex list before they know
235 /// which entries the faces use, and it has no step that revisits the
236 /// question -- `RemoveUnusedValues` exists there but is compiled into the
237 /// transcoder alone.
238 pub fn remove_points_unused_by_faces(&mut self) {
239 let num_points = self.num_points();
240 if num_points == 0 {
241 return;
242 }
243 // A face naming a point this mesh does not have describes nothing: a
244 // PLY carries face indices straight from the file, so the count and
245 // the indices need not agree. Such a face is dropped rather than
246 // renumbered -- renumbering it would invent a point for it, which is
247 // what the face-order renumbering this replaced used to do, and what
248 // left writers emitting indices their own readers refuse.
249 self.faces
250 .retain(|face| face.iter().all(|corner| (corner.0 as usize) < num_points));
251
252 let mut used = vec![false; num_points];
253 for face in &self.faces {
254 for corner in face.iter() {
255 used[corner.0 as usize] = true;
256 }
257 }
258 let num_used = used.iter().filter(|u| **u).count();
259 if num_used == num_points {
260 // Still worth the second half: an attribute can carry values no
261 // point names even when every point is named by a face.
262 self.remove_unused_attribute_values();
263 return;
264 }
265
266 let mut old_to_new = vec![u32::MAX; num_points];
267 let mut next = 0u32;
268 for (point, keep) in used.iter().enumerate() {
269 if *keep {
270 old_to_new[point] = next;
271 next += 1;
272 }
273 }
274 for face in &mut self.faces {
275 for corner in face.iter_mut() {
276 // Same as above: a corner naming no point of this mesh is left
277 // alone for the encoder to refuse.
278 if let Some(new) = old_to_new.get(corner.0 as usize) {
279 *corner = PointIndex(*new);
280 }
281 }
282 }
283 for att_id in 0..self.point_cloud.num_attributes() {
284 let kept: Vec<AttributeValueIndex> = (0..num_points)
285 .filter(|point| used[*point])
286 .map(|point| {
287 self.point_cloud
288 .attribute(att_id)
289 .mapped_index(PointIndex(point as u32))
290 })
291 .collect();
292 self.point_cloud
293 .attribute_mut(att_id)
294 .set_explicit_mapping_from(&kept);
295 }
296 self.point_cloud.set_num_points(num_used);
297 self.remove_unused_attribute_values();
298 }
299
300 /// Drops attribute values no point maps to.
301 fn remove_unused_attribute_values(&mut self) {
302 for att_id in 0..self.point_cloud.num_attributes() {
303 self.point_cloud
304 .attribute_mut(att_id)
305 .remove_unused_values();
306 }
307 }
308
309 /// Renumbers points into the order the faces first name them, dropping any
310 /// point no face names at all.
311 ///
312 /// Not a deduplication, despite what this was once called, and not
313 /// upstream's operation: [`deduplicate_point_ids`](Self::deduplicate_point_ids)
314 /// merges points whose values coincide and keeps the order they arrived
315 /// in, while this one merges nothing and reorders everything.
316 ///
317 /// What it reproduces is the *numbering* upstream's OBJ reader ends up
318 /// with, because that reader emits one point per face corner and its
319 /// point order is therefore corner order already. A reader whose points
320 /// arrive as a vertex list -- PLY, glTF -- gets a different numbering from
321 /// this than upstream gets from its own pair, so it is not a substitute
322 /// for them.
323 pub fn renumber_points_in_face_order(&mut self) {
324 if self.faces.is_empty() || self.num_points() == 0 {
325 return;
326 }
327
328 // Build mapping from old point ID to new point ID
329 // Points are assigned new IDs in the order they're first seen in faces
330 let mut old_to_new: HashMap<u32, u32> = HashMap::new();
331 let mut new_id = 0u32;
332
333 // First pass: determine the mapping
334 for face in &self.faces {
335 for &point_idx in face.iter() {
336 if let std::collections::hash_map::Entry::Vacant(e) = old_to_new.entry(point_idx.0)
337 {
338 e.insert(new_id);
339 new_id += 1;
340 }
341 }
342 }
343
344 // If no remapping needed (already in correct order), skip
345 let needs_remap = old_to_new.iter().any(|(&old, &new)| old != new);
346 if !needs_remap {
347 return;
348 }
349
350 // Build reverse mapping for reordering attributes
351 let num_unique = new_id as usize;
352 let mut new_to_old = vec![0u32; num_unique];
353 for (&old, &new) in &old_to_new {
354 new_to_old[new as usize] = old;
355 }
356
357 // Second pass: update face indices
358 for face in &mut self.faces {
359 for point_idx in face.iter_mut() {
360 point_idx.0 = old_to_new[&point_idx.0];
361 }
362 }
363
364 // Third pass: reorder attribute data
365 // For each attribute, create new buffer with data in new order
366 for att_idx in 0..self.num_attributes() {
367 let att = self.attribute(att_idx);
368 let stride = att.byte_stride() as usize;
369 let old_buffer = att.buffer().data().to_vec();
370
371 // Create new buffer with reordered data
372 let mut new_buffer = vec![0u8; num_unique * stride];
373 for new_idx in 0..num_unique {
374 let old_idx = new_to_old[new_idx] as usize;
375 if old_idx * stride + stride <= old_buffer.len() {
376 new_buffer[new_idx * stride..new_idx * stride + stride]
377 .copy_from_slice(&old_buffer[old_idx * stride..old_idx * stride + stride]);
378 }
379 }
380
381 // Update the attribute through `resize_unique_entries` rather than
382 // resizing its buffer directly: the buffer is only half of an
383 // attribute's size, and leaving `size()` at the pre-dedup count
384 // makes the attribute claim entries its buffer no longer holds.
385 // Anything that walks the attribute by `size()` then reads past the
386 // end -- reachable from any mesh with vertices no face references,
387 // which is ordinary in scanned geometry.
388 let att_mut = self.attribute_mut(att_idx);
389 if att_mut.resize_unique_entries(num_unique).is_ok() {
390 att_mut.buffer_mut().write(0, &new_buffer);
391 }
392 }
393
394 // Update point count
395 self.set_num_points(num_unique);
396 }
397}
398
399impl Deref for Mesh {
400 type Target = PointCloud;
401
402 fn deref(&self) -> &Self::Target {
403 &self.point_cloud
404 }
405}
406
407impl DerefMut for Mesh {
408 fn deref_mut(&mut self) -> &mut Self::Target {
409 &mut self.point_cloud
410 }
411}
412
413#[cfg(test)]
414mod tests {
415 use super::*;
416 use crate::draco_types::DataType;
417 use crate::geometry_attribute::{GeometryAttributeType, PointAttribute};
418
419 /// A mesh whose vertices are not all referenced by faces -- ordinary in
420 /// scanned geometry, where the raw point set outlives the triangulation.
421 ///
422 /// Deduplication drops the unreferenced ones, and every attribute has to
423 /// come away describing the points that are left. It used to rewrite the
424 /// buffer but leave `size()` at the old count, so the attribute claimed
425 /// entries whose bytes were gone and readers walked off the end.
426 #[test]
427 fn renumbering_shrinks_attribute_size_with_its_buffer() {
428 let mut mesh = Mesh::new();
429 let num_points = 5;
430 mesh.set_num_points(num_points);
431 mesh.set_num_faces(1);
432
433 let mut attribute = PointAttribute::new();
434 attribute.init(
435 GeometryAttributeType::Position,
436 3,
437 DataType::Float32,
438 false,
439 num_points,
440 );
441 for point in 0..num_points {
442 for component in 0..3 {
443 let value = (point * 3 + component) as f32;
444 attribute
445 .buffer_mut()
446 .update(&value.to_le_bytes(), Some((point * 3 + component) * 4));
447 }
448 }
449 mesh.add_attribute(attribute);
450
451 // Only three of the five points are reachable through a face.
452 mesh.set_face(FaceIndex(0), [PointIndex(4), PointIndex(2), PointIndex(0)]);
453
454 mesh.renumber_points_in_face_order();
455
456 assert_eq!(
457 mesh.num_points(),
458 3,
459 "unreferenced points should be dropped"
460 );
461 let attribute = mesh.attribute(0);
462 assert_eq!(
463 attribute.size(),
464 3,
465 "attribute still claims entries it no longer stores"
466 );
467 assert_eq!(
468 attribute.buffer().data().len(),
469 3 * attribute.byte_stride() as usize,
470 "buffer and size disagree"
471 );
472
473 // The surviving values must be the ones the faces pointed at, in the
474 // order the faces first reach them.
475 let read = |entry: usize| -> f32 {
476 let offset = entry * attribute.byte_stride() as usize;
477 f32::from_le_bytes(
478 attribute.buffer().data()[offset..offset + 4]
479 .try_into()
480 .unwrap(),
481 )
482 };
483 assert_eq!(read(0), 12.0, "first face corner was old point 4");
484 assert_eq!(read(1), 6.0, "second face corner was old point 2");
485 assert_eq!(read(2), 0.0, "third face corner was old point 0");
486 }
487}