Skip to main content

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