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    /// 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}