parry3d/shape/
trimesh.rs

1use crate::bounding_volume::Aabb;
2use crate::math::{Isometry, Point, Real, Vector};
3use crate::partitioning::{Bvh, BvhBuildStrategy};
4use crate::shape::{FeatureId, Shape, Triangle, TrianglePseudoNormals, TypedCompositeShape};
5use crate::utils::HashablePartialEq;
6use alloc::{vec, vec::Vec};
7use core::fmt;
8#[cfg(feature = "dim3")]
9use {crate::shape::Cuboid, crate::utils::SortedPair, na::Unit};
10
11use {
12    crate::shape::composite_shape::CompositeShape,
13    crate::utils::hashmap::{Entry, HashMap},
14    crate::utils::hashset::HashSet,
15};
16
17#[cfg(feature = "dim2")]
18use crate::transformation::ear_clipping::triangulate_ear_clipping;
19
20use crate::query::details::NormalConstraints;
21#[cfg(feature = "rkyv")]
22use rkyv::{bytecheck, CheckBytes};
23
24/// Indicated an inconsistency in the topology of a triangle mesh.
25#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq)]
26pub enum TopologyError {
27    /// Found a triangle with two or three identical vertices.
28    #[error("the triangle {0} has at least two identical vertices.")]
29    BadTriangle(u32),
30    /// At least two adjacent triangles have opposite orientations.
31    #[error("the triangles {triangle1} and {triangle2} sharing the edge {edge:?} have opposite orientations.")]
32    BadAdjacentTrianglesOrientation {
33        /// The first triangle, with an orientation opposite to the second triangle.
34        triangle1: u32,
35        /// The second triangle, with an orientation opposite to the first triangle.
36        triangle2: u32,
37        /// The edge shared between the two triangles.
38        edge: (u32, u32),
39    },
40}
41
42/// Indicated an inconsistency while building a triangle mesh.
43#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq)]
44pub enum TriMeshBuilderError {
45    /// A triangle mesh must contain at least one triangle.
46    #[error("A triangle mesh must contain at least one triangle.")]
47    EmptyIndices,
48    /// Indicated an inconsistency in the topology of a triangle mesh.
49    #[error("Topology Error: {0}")]
50    TopologyError(TopologyError),
51}
52
53/// The set of pseudo-normals of a triangle mesh.
54///
55/// These pseudo-normals are used for the inside-outside test of a
56/// point on the triangle, as described in the paper:
57/// "Signed distance computation using the angle weighted pseudonormal", Baerentzen, et al.
58/// DOI: 10.1109/TVCG.2005.49
59#[derive(Default, Clone)]
60#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
61#[cfg_attr(
62    feature = "rkyv",
63    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
64    archive(check_bytes)
65)]
66#[repr(C)]
67#[cfg(feature = "dim3")]
68pub struct TriMeshPseudoNormals {
69    /// The pseudo-normals of the vertices.
70    pub vertices_pseudo_normal: Vec<Vector<Real>>,
71    /// The pseudo-normals of the edges.
72    pub edges_pseudo_normal: Vec<[Vector<Real>; 3]>,
73}
74
75/// The connected-components of a triangle mesh.
76#[derive(Debug, Clone)]
77#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
78#[cfg_attr(
79    feature = "rkyv",
80    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
81    archive(check_bytes)
82)]
83#[repr(C)]
84pub struct TriMeshConnectedComponents {
85    /// The `face_colors[i]` gives the connected-component index
86    /// of the i-th face.
87    pub face_colors: Vec<u32>,
88    /// The set of faces grouped by connected components.
89    pub grouped_faces: Vec<u32>,
90    /// The range of connected components. `self.grouped_faces[self.ranges[i]..self.ranges[i + 1]]`
91    /// contains the indices of all the faces part of the i-th connected component.
92    pub ranges: Vec<usize>,
93}
94
95impl TriMeshConnectedComponents {
96    /// The total number of connected components.
97    pub fn num_connected_components(&self) -> usize {
98        self.ranges.len() - 1
99    }
100
101    /// Convert the connected-component description into actual meshes (returned as raw index and
102    /// vertex buffers).
103    ///
104    /// The `mesh` must be the one used to generate `self`, otherwise it might panic or produce an
105    /// unexpected result.
106    pub fn to_mesh_buffers(&self, mesh: &TriMesh) -> Vec<(Vec<Point<Real>>, Vec<[u32; 3]>)> {
107        let mut result = vec![];
108        let mut new_vtx_index: Vec<_> = vec![u32::MAX; mesh.vertices.len()];
109
110        for ranges in self.ranges.windows(2) {
111            let num_faces = ranges[1] - ranges[0];
112
113            if num_faces == 0 {
114                continue;
115            }
116
117            let mut vertices = Vec::with_capacity(num_faces);
118            let mut indices = Vec::with_capacity(num_faces);
119
120            for fid in ranges[0]..ranges[1] {
121                let vids = mesh.indices[self.grouped_faces[fid] as usize];
122                let new_vids = vids.map(|id| {
123                    if new_vtx_index[id as usize] == u32::MAX {
124                        vertices.push(mesh.vertices[id as usize]);
125                        new_vtx_index[id as usize] = vertices.len() as u32 - 1;
126                    }
127
128                    new_vtx_index[id as usize]
129                });
130                indices.push(new_vids);
131            }
132
133            result.push((vertices, indices));
134        }
135
136        result
137    }
138
139    /// Convert the connected-component description into actual meshes.
140    ///
141    /// The `mesh` must be the one used to generate `self`, otherwise it might panic or produce an
142    /// unexpected result.
143    ///
144    /// All the meshes are constructed with the given `flags`.
145    pub fn to_meshes(
146        &self,
147        mesh: &TriMesh,
148        flags: TriMeshFlags,
149    ) -> Vec<Result<TriMesh, TriMeshBuilderError>> {
150        self.to_mesh_buffers(mesh)
151            .into_iter()
152            .map(|(vtx, idx)| TriMesh::with_flags(vtx, idx, flags))
153            .collect()
154    }
155}
156
157/// A vertex of a triangle-mesh’s half-edge topology.
158#[derive(Clone, Copy, Debug)]
159#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
160#[cfg_attr(
161    feature = "rkyv",
162    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
163    archive(as = "Self")
164)]
165#[repr(C)]
166pub struct TopoVertex {
167    /// One of the half-edge with this vertex as endpoint.
168    pub half_edge: u32,
169}
170
171/// A face of a triangle-mesh’s half-edge topology.
172#[derive(Clone, Copy, Debug)]
173#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
174#[cfg_attr(
175    feature = "rkyv",
176    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
177    archive(as = "Self")
178)]
179#[repr(C)]
180pub struct TopoFace {
181    /// The half-edge adjacent to this face, with a starting point equal
182    /// to the first point of this face.
183    pub half_edge: u32,
184}
185
186/// A half-edge of a triangle-mesh’s half-edge topology.
187#[derive(Clone, Copy, Debug)]
188#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
189#[cfg_attr(
190    feature = "rkyv",
191    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
192    archive(as = "Self")
193)]
194#[repr(C)]
195pub struct TopoHalfEdge {
196    /// The next half-edge.
197    pub next: u32,
198    /// This half-edge twin on the adjacent triangle.
199    ///
200    /// This is `u32::MAX` if there is no twin.
201    pub twin: u32,
202    /// The first vertex of this edge.
203    pub vertex: u32,
204    /// The face associated to this half-edge.
205    pub face: u32,
206}
207
208/// The half-edge topology information of a triangle mesh.
209#[derive(Default, Clone)]
210#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
211#[cfg_attr(
212    feature = "rkyv",
213    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
214    archive(check_bytes)
215)]
216#[repr(C)]
217pub struct TriMeshTopology {
218    /// The vertices of this half-edge representation.
219    pub vertices: Vec<TopoVertex>,
220    /// The faces of this half-edge representation.
221    pub faces: Vec<TopoFace>,
222    /// The half-edges of this half-edge representation.
223    pub half_edges: Vec<TopoHalfEdge>,
224}
225
226#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
227#[cfg_attr(
228    feature = "rkyv",
229    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
230    archive(as = "Self")
231)]
232#[repr(C)]
233#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
234/// Controls how a [`TriMesh`] should be loaded.
235pub struct TriMeshFlags(u16);
236
237bitflags::bitflags! {
238    impl TriMeshFlags: u16 {
239        /// If set, the half-edge topology of the trimesh will be computed if possible.
240        const HALF_EDGE_TOPOLOGY = 1;
241        /// If set, the connected components of the trimesh will be computed.
242        const CONNECTED_COMPONENTS = 1 << 1;
243        /// If set, any triangle that results in a failing half-hedge topology computation will be deleted.
244        const DELETE_BAD_TOPOLOGY_TRIANGLES = 1 << 2;
245        /// If set, the trimesh will be assumed to be oriented (with outward normals).
246        ///
247        /// The pseudo-normals of its vertices and edges will be computed.
248        const ORIENTED = 1 << 3;
249        /// If set, the duplicate vertices of the trimesh will be merged.
250        ///
251        /// Two vertices with the exact same coordinates will share the same entry on the
252        /// vertex buffer and the index buffer is adjusted accordingly.
253        const MERGE_DUPLICATE_VERTICES = 1 << 4;
254        /// If set, the triangles sharing two vertices with identical index values will be removed.
255        ///
256        /// Because of the way it is currently implemented, this methods implies that duplicate
257        /// vertices will be merged. It will no longer be the case in the future once we decouple
258        /// the computations.
259        const DELETE_DEGENERATE_TRIANGLES = 1 << 5;
260        /// If set, two triangles sharing three vertices with identical index values (in any order)
261        /// will be removed.
262        ///
263        /// Because of the way it is currently implemented, this methods implies that duplicate
264        /// vertices will be merged. It will no longer be the case in the future once we decouple
265        /// the computations.
266        const DELETE_DUPLICATE_TRIANGLES = 1 << 6;
267        /// If set, a special treatment will be applied to contact manifold calculation to eliminate
268        /// or fix contacts normals that could lead to incorrect bumps in physics simulation
269        /// (especially on flat surfaces).
270        ///
271        /// This is achieved by taking into account adjacent triangle normals when computing contact
272        /// points for a given triangle.
273        const FIX_INTERNAL_EDGES = (1 << 7) | Self::MERGE_DUPLICATE_VERTICES.bits();
274    }
275}
276
277#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
278#[cfg_attr(
279    feature = "rkyv",
280    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
281    archive(check_bytes)
282)]
283#[repr(C)]
284#[derive(Clone)]
285/// A triangle mesh.
286pub struct TriMesh {
287    bvh: Bvh,
288    vertices: Vec<Point<Real>>,
289    indices: Vec<[u32; 3]>,
290    #[cfg(feature = "dim3")]
291    pub(crate) pseudo_normals: Option<TriMeshPseudoNormals>,
292    topology: Option<TriMeshTopology>,
293    connected_components: Option<TriMeshConnectedComponents>,
294    flags: TriMeshFlags,
295}
296
297impl fmt::Debug for TriMesh {
298    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299        write!(f, "GenericTriMesh")
300    }
301}
302
303impl TriMesh {
304    /// Creates a new triangle mesh from a vertex buffer and an index buffer.
305    pub fn new(
306        vertices: Vec<Point<Real>>,
307        indices: Vec<[u32; 3]>,
308    ) -> Result<Self, TriMeshBuilderError> {
309        Self::with_flags(vertices, indices, TriMeshFlags::empty())
310    }
311
312    /// Creates a new triangle mesh from a vertex buffer and an index buffer, and flags controlling optional properties.
313    pub fn with_flags(
314        vertices: Vec<Point<Real>>,
315        indices: Vec<[u32; 3]>,
316        flags: TriMeshFlags,
317    ) -> Result<Self, TriMeshBuilderError> {
318        if indices.is_empty() {
319            return Err(TriMeshBuilderError::EmptyIndices);
320        }
321
322        let mut result = Self {
323            bvh: Bvh::new(),
324            vertices,
325            indices,
326            #[cfg(feature = "dim3")]
327            pseudo_normals: None,
328            topology: None,
329            connected_components: None,
330            flags: TriMeshFlags::empty(),
331        };
332
333        let _ = result.set_flags(flags);
334
335        if result.bvh.is_empty() {
336            // The BVH hasn’t been computed by `.set_flags`.
337            result.rebuild_bvh();
338        }
339
340        Ok(result)
341    }
342
343    /// Sets the flags of this triangle mesh, controlling its optional associated data.
344    pub fn set_flags(&mut self, flags: TriMeshFlags) -> Result<(), TopologyError> {
345        let mut result = Ok(());
346        let prev_indices_len = self.indices.len();
347
348        if !flags.contains(TriMeshFlags::HALF_EDGE_TOPOLOGY) {
349            self.topology = None;
350        }
351
352        #[cfg(feature = "dim3")]
353        if !flags.intersects(TriMeshFlags::ORIENTED | TriMeshFlags::FIX_INTERNAL_EDGES) {
354            self.pseudo_normals = None;
355        }
356
357        if !flags.contains(TriMeshFlags::CONNECTED_COMPONENTS) {
358            self.connected_components = None;
359        }
360
361        let difference = flags & !self.flags;
362
363        if difference.intersects(
364            TriMeshFlags::MERGE_DUPLICATE_VERTICES
365                | TriMeshFlags::DELETE_DEGENERATE_TRIANGLES
366                | TriMeshFlags::DELETE_DUPLICATE_TRIANGLES,
367        ) {
368            self.merge_duplicate_vertices(
369                flags.contains(TriMeshFlags::DELETE_DEGENERATE_TRIANGLES),
370                flags.contains(TriMeshFlags::DELETE_DUPLICATE_TRIANGLES),
371            )
372        }
373
374        if difference.intersects(
375            TriMeshFlags::HALF_EDGE_TOPOLOGY | TriMeshFlags::DELETE_BAD_TOPOLOGY_TRIANGLES,
376        ) {
377            result =
378                self.compute_topology(flags.contains(TriMeshFlags::DELETE_BAD_TOPOLOGY_TRIANGLES));
379        }
380
381        #[cfg(feature = "std")]
382        if difference.intersects(TriMeshFlags::CONNECTED_COMPONENTS) {
383            self.compute_connected_components();
384        }
385
386        #[cfg(feature = "dim3")]
387        if difference.intersects(TriMeshFlags::ORIENTED | TriMeshFlags::FIX_INTERNAL_EDGES) {
388            self.compute_pseudo_normals();
389        }
390
391        if prev_indices_len != self.indices.len() {
392            self.rebuild_bvh();
393        }
394
395        self.flags = flags;
396        result
397    }
398
399    // TODO: support a crate like get_size2 (will require support on nalgebra too)?
400    /// An approximation of the memory usage (in bytes) for this struct plus
401    /// the memory it allocates dynamically.
402    pub fn total_memory_size(&self) -> usize {
403        size_of::<Self>() + self.heap_memory_size()
404    }
405
406    /// An approximation of the memory dynamically-allocated by this struct.
407    pub fn heap_memory_size(&self) -> usize {
408        // NOTE: if a new field is added to `Self`, adjust this function result.
409        let Self {
410            bvh,
411            vertices,
412            indices,
413            topology,
414            connected_components,
415            flags: _,
416            #[cfg(feature = "dim3")]
417            pseudo_normals,
418        } = self;
419        let sz_bvh = bvh.heap_memory_size();
420        let sz_vertices = vertices.capacity() * size_of::<Point<Real>>();
421        let sz_indices = indices.capacity() * size_of::<[u32; 3]>();
422        #[cfg(feature = "dim3")]
423        let sz_pseudo_normals = pseudo_normals
424            .as_ref()
425            .map(|pn| {
426                pn.vertices_pseudo_normal.capacity() * size_of::<Vector<Real>>()
427                    + pn.edges_pseudo_normal.capacity() * size_of::<[Vector<Real>; 3]>()
428            })
429            .unwrap_or(0);
430        #[cfg(feature = "dim2")]
431        let sz_pseudo_normals = 0;
432        let sz_topology = topology
433            .as_ref()
434            .map(|t| {
435                t.vertices.capacity() * size_of::<TopoVertex>()
436                    + t.faces.capacity() * size_of::<TopoFace>()
437                    + t.half_edges.capacity() * size_of::<TopoHalfEdge>()
438            })
439            .unwrap_or(0);
440        let sz_connected_components = connected_components
441            .as_ref()
442            .map(|c| {
443                c.face_colors.capacity() * size_of::<u32>()
444                    + c.grouped_faces.capacity() * size_of::<f32>()
445                    + c.ranges.capacity() * size_of::<usize>()
446            })
447            .unwrap_or(0);
448
449        sz_bvh
450            + sz_vertices
451            + sz_indices
452            + sz_pseudo_normals
453            + sz_topology
454            + sz_connected_components
455    }
456
457    /// Transforms in-place the vertices of this triangle mesh.
458    pub fn transform_vertices(&mut self, transform: &Isometry<Real>) {
459        self.vertices
460            .iter_mut()
461            .for_each(|pt| *pt = transform * *pt);
462        self.rebuild_bvh();
463
464        // The pseudo-normals must be rotated too.
465        #[cfg(feature = "dim3")]
466        if let Some(pseudo_normals) = &mut self.pseudo_normals {
467            pseudo_normals
468                .vertices_pseudo_normal
469                .iter_mut()
470                .for_each(|n| *n = transform * *n);
471            pseudo_normals.edges_pseudo_normal.iter_mut().for_each(|n| {
472                n[0] = transform * n[0];
473                n[1] = transform * n[1];
474                n[2] = transform * n[2];
475            });
476        }
477    }
478
479    /// Returns a scaled version of this triangle mesh.
480    pub fn scaled(mut self, scale: &Vector<Real>) -> Self {
481        self.vertices
482            .iter_mut()
483            .for_each(|pt| pt.coords.component_mul_assign(scale));
484
485        #[cfg(feature = "dim3")]
486        if let Some(pn) = &mut self.pseudo_normals {
487            pn.vertices_pseudo_normal.iter_mut().for_each(|n| {
488                n.component_mul_assign(scale);
489                let _ = n.try_normalize_mut(0.0);
490            });
491            pn.edges_pseudo_normal.iter_mut().for_each(|n| {
492                n[0].component_mul_assign(scale);
493                n[1].component_mul_assign(scale);
494                n[2].component_mul_assign(scale);
495
496                let _ = n[0].try_normalize_mut(0.0);
497                let _ = n[1].try_normalize_mut(0.0);
498                let _ = n[2].try_normalize_mut(0.0);
499            });
500        }
501
502        let mut bvh = self.bvh.clone();
503        bvh.scale(scale);
504
505        Self {
506            bvh,
507            vertices: self.vertices,
508            indices: self.indices,
509            #[cfg(feature = "dim3")]
510            pseudo_normals: self.pseudo_normals,
511            topology: self.topology,
512            connected_components: self.connected_components,
513            flags: self.flags,
514        }
515    }
516
517    /// Appends a second triangle mesh to this triangle mesh.
518    pub fn append(&mut self, rhs: &TriMesh) {
519        let base_id = self.vertices.len() as u32;
520        self.vertices.extend_from_slice(rhs.vertices());
521        self.indices.extend(
522            rhs.indices()
523                .iter()
524                .map(|idx| [idx[0] + base_id, idx[1] + base_id, idx[2] + base_id]),
525        );
526
527        let vertices = core::mem::take(&mut self.vertices);
528        let indices = core::mem::take(&mut self.indices);
529        *self = TriMesh::with_flags(vertices, indices, self.flags).unwrap();
530    }
531
532    /// Create a `TriMesh` from a set of points assumed to describe a counter-clockwise non-convex polygon.
533    ///
534    /// This operation may fail if the input polygon is invalid, e.g. it is non-simple or has zero surface area.
535    #[cfg(feature = "dim2")]
536    pub fn from_polygon(vertices: Vec<Point<Real>>) -> Option<Self> {
537        triangulate_ear_clipping(&vertices).map(|indices| Self::new(vertices, indices).unwrap())
538    }
539
540    /// A flat view of the index buffer of this mesh.
541    pub fn flat_indices(&self) -> &[u32] {
542        unsafe {
543            let len = self.indices.len() * 3;
544            let data = self.indices.as_ptr() as *const u32;
545            core::slice::from_raw_parts(data, len)
546        }
547    }
548
549    fn rebuild_bvh(&mut self) {
550        let leaves = self.indices.iter().enumerate().map(|(i, idx)| {
551            let aabb = Triangle::new(
552                self.vertices[idx[0] as usize],
553                self.vertices[idx[1] as usize],
554                self.vertices[idx[2] as usize],
555            )
556            .local_aabb();
557            (i, aabb)
558        });
559
560        self.bvh = Bvh::from_iter(BvhBuildStrategy::Binned, leaves)
561    }
562
563    /// Reverse the orientation of the triangle mesh.
564    pub fn reverse(&mut self) {
565        self.indices.iter_mut().for_each(|idx| idx.swap(0, 1));
566
567        // NOTE: the BVH, and connected components are not changed by this operation.
568        //       The pseudo-normals just have to be flipped.
569        //       The topology must be recomputed.
570
571        #[cfg(feature = "dim3")]
572        if let Some(pseudo_normals) = &mut self.pseudo_normals {
573            for n in &mut pseudo_normals.vertices_pseudo_normal {
574                *n = -*n;
575            }
576
577            for n in pseudo_normals.edges_pseudo_normal.iter_mut() {
578                n[0] = -n[0];
579                n[1] = -n[1];
580                n[2] = -n[2];
581            }
582        }
583
584        if self.flags.contains(TriMeshFlags::HALF_EDGE_TOPOLOGY) {
585            // TODO: this could be done more efficiently.
586            let _ = self.compute_topology(false);
587        }
588    }
589
590    /// Merge all duplicate vertices and adjust the index buffer accordingly.
591    ///
592    /// If `delete_degenerate_triangles` is set to true, any triangle with two
593    /// identical vertices will be removed.
594    ///
595    /// This is typically used to recover a vertex buffer from which we can deduce
596    /// adjacency information. between triangles by observing how the vertices are
597    /// shared by triangles based on the index buffer.
598    fn merge_duplicate_vertices(
599        &mut self,
600        delete_degenerate_triangles: bool,
601        delete_duplicate_triangles: bool,
602    ) {
603        let mut vtx_to_id = HashMap::default();
604        let mut new_vertices = Vec::with_capacity(self.vertices.len());
605        let mut new_indices = Vec::with_capacity(self.indices.len());
606        let mut triangle_set = HashSet::default();
607
608        fn resolve_coord_id(
609            coord: &Point<Real>,
610            vtx_to_id: &mut HashMap<HashablePartialEq<Point<Real>>, u32>,
611            new_vertices: &mut Vec<Point<Real>>,
612        ) -> u32 {
613            let key = HashablePartialEq::new(*coord);
614            let id = match vtx_to_id.entry(key) {
615                Entry::Occupied(entry) => entry.into_mut(),
616                Entry::Vacant(entry) => entry.insert(new_vertices.len() as u32),
617            };
618
619            if *id == new_vertices.len() as u32 {
620                new_vertices.push(*coord);
621            }
622
623            *id
624        }
625
626        for t in self.indices.iter() {
627            let va = resolve_coord_id(
628                &self.vertices[t[0] as usize],
629                &mut vtx_to_id,
630                &mut new_vertices,
631            );
632
633            let vb = resolve_coord_id(
634                &self.vertices[t[1] as usize],
635                &mut vtx_to_id,
636                &mut new_vertices,
637            );
638
639            let vc = resolve_coord_id(
640                &self.vertices[t[2] as usize],
641                &mut vtx_to_id,
642                &mut new_vertices,
643            );
644
645            let is_degenerate = va == vb || va == vc || vb == vc;
646
647            if !is_degenerate || !delete_degenerate_triangles {
648                if delete_duplicate_triangles {
649                    let (c, b, a) = crate::utils::sort3(&va, &vb, &vc);
650                    if triangle_set.insert((*a, *b, *c)) {
651                        new_indices.push([va, vb, vc])
652                    }
653                } else {
654                    new_indices.push([va, vb, vc]);
655                }
656            }
657        }
658
659        new_vertices.shrink_to_fit();
660
661        self.vertices = new_vertices;
662        self.indices = new_indices;
663
664        // Vertices and indices changed: the pseudo-normals are no longer valid.
665        #[cfg(feature = "dim3")]
666        if self.pseudo_normals.is_some() {
667            self.compute_pseudo_normals();
668        }
669
670        // Vertices and indices changed: the topology no longer valid.
671        #[cfg(feature = "dim3")]
672        if self.topology.is_some() {
673            let _ = self.compute_topology(false);
674        }
675    }
676
677    #[cfg(feature = "dim3")]
678    /// Computes the pseudo-normals used for solid point-projection.
679    ///
680    /// This computes the pseudo-normals needed by the point containment test described in
681    /// "Signed distance computation using the angle weighted pseudonormal", Baerentzen, et al.
682    /// DOI: 10.1109/TVCG.2005.49
683    ///
684    /// For the point-containment test to properly detect the inside of the trimesh (i.e. to return
685    /// `proj.is_inside = true`), the trimesh must:
686    /// - Be manifold (closed, no t-junctions, etc.)
687    /// - Be oriented with outward normals.
688    ///
689    /// If the trimesh is correctly oriented, but is manifold everywhere except at its boundaries,
690    /// then the computed pseudo-normals will provide correct point-containment test results except
691    /// for points closest to the boundary of the mesh.
692    ///
693    /// It may be useful to call `self.remove_duplicate_vertices()` before this method, in order to fix the
694    /// index buffer if some of the vertices of this trimesh are duplicated.
695    fn compute_pseudo_normals(&mut self) {
696        let mut vertices_pseudo_normal = vec![Vector::zeros(); self.vertices().len()];
697        let mut edges_pseudo_normal = HashMap::default();
698        let mut edges_multiplicity = HashMap::default();
699
700        for idx in self.indices() {
701            let vtx = self.vertices();
702            let tri = Triangle::new(
703                vtx[idx[0] as usize],
704                vtx[idx[1] as usize],
705                vtx[idx[2] as usize],
706            );
707
708            if let Some(n) = tri.normal() {
709                let ang1 = (tri.b - tri.a).angle(&(tri.c - tri.a));
710                let ang2 = (tri.a - tri.b).angle(&(tri.c - tri.b));
711                let ang3 = (tri.b - tri.c).angle(&(tri.a - tri.c));
712
713                vertices_pseudo_normal[idx[0] as usize] += *n * ang1;
714                vertices_pseudo_normal[idx[1] as usize] += *n * ang2;
715                vertices_pseudo_normal[idx[2] as usize] += *n * ang3;
716
717                let edges = [
718                    SortedPair::new(idx[0], idx[1]),
719                    SortedPair::new(idx[0], idx[2]),
720                    SortedPair::new(idx[1], idx[2]),
721                ];
722
723                for edge in &edges {
724                    let edge_n = edges_pseudo_normal
725                        .entry(*edge)
726                        .or_insert_with(Vector::zeros);
727                    *edge_n += *n; // NOTE: there is no need to multiply by the incident angle since it is always equal to PI for all the edges.
728                    let edge_mult = edges_multiplicity.entry(*edge).or_insert(0);
729                    *edge_mult += 1;
730                }
731            }
732        }
733
734        let edges_pseudo_normal = self
735            .indices()
736            .iter()
737            .map(|idx| {
738                let e0 = SortedPair::new(idx[0], idx[1]);
739                let e1 = SortedPair::new(idx[1], idx[2]);
740                let e2 = SortedPair::new(idx[2], idx[0]);
741                let default = Vector::zeros();
742                [
743                    edges_pseudo_normal.get(&e0).copied().unwrap_or(default),
744                    edges_pseudo_normal.get(&e1).copied().unwrap_or(default),
745                    edges_pseudo_normal.get(&e2).copied().unwrap_or(default),
746                ]
747            })
748            .collect();
749
750        self.pseudo_normals = Some(TriMeshPseudoNormals {
751            vertices_pseudo_normal,
752            edges_pseudo_normal,
753        })
754    }
755
756    fn delete_bad_topology_triangles(&mut self) {
757        let mut half_edge_set = HashSet::default();
758        let mut deleted_any = false;
759
760        // First, create three half-edges for each face.
761        self.indices.retain(|idx| {
762            if idx[0] == idx[1] || idx[0] == idx[2] || idx[1] == idx[2] {
763                deleted_any = true;
764                return false;
765            }
766
767            for k in 0..3 {
768                let edge_key = (idx[k as usize], idx[(k as usize + 1) % 3]);
769                if half_edge_set.contains(&edge_key) {
770                    deleted_any = true;
771                    return false;
772                }
773            }
774
775            for k in 0..3 {
776                let edge_key = (idx[k as usize], idx[(k as usize + 1) % 3]);
777                let _ = half_edge_set.insert(edge_key);
778            }
779
780            true
781        });
782    }
783
784    /// Computes half-edge topological information for this triangle mesh, based on its index buffer only.
785    ///
786    /// This computes the half-edge representation of this triangle mesh’s topology. This is useful for advanced
787    /// geometric operations like trimesh-trimesh intersection geometry computation.
788    ///
789    /// It may be useful to call `self.merge_duplicate_vertices(true, true)` before this method, in order to fix the
790    /// index buffer if some of the vertices of this trimesh are duplicated.
791    ///
792    /// # Return
793    /// Returns `true` if the computation succeeded. Returns `false` if this mesh can’t have an half-edge representation
794    /// because at least three faces share the same edge.
795    fn compute_topology(&mut self, delete_bad_triangles: bool) -> Result<(), TopologyError> {
796        if delete_bad_triangles {
797            self.delete_bad_topology_triangles();
798        }
799
800        let mut topology = TriMeshTopology::default();
801        let mut half_edge_map = HashMap::default();
802
803        topology.vertices.resize(
804            self.vertices.len(),
805            TopoVertex {
806                half_edge: u32::MAX,
807            },
808        );
809
810        // First, create three half-edges for each face.
811        for (fid, idx) in self.indices.iter().enumerate() {
812            let half_edge_base_id = topology.half_edges.len() as u32;
813
814            if idx[0] == idx[1] || idx[0] == idx[2] || idx[1] == idx[2] {
815                return Err(TopologyError::BadTriangle(fid as u32));
816            }
817
818            for k in 0u32..3 {
819                let half_edge = TopoHalfEdge {
820                    next: half_edge_base_id + (k + 1) % 3,
821                    // We don’t know which one it is yet.
822                    // If the twin doesn’t exist, we use `u32::MAX` as
823                    // it’s (invalid) index. This value can be relied on
824                    // by other algorithms.
825                    twin: u32::MAX,
826                    vertex: idx[k as usize],
827                    face: fid as u32,
828                };
829                topology.half_edges.push(half_edge);
830
831                let edge_key = (idx[k as usize], idx[(k as usize + 1) % 3]);
832
833                if let Some(existing) = half_edge_map.insert(edge_key, half_edge_base_id + k) {
834                    // If the same edge already exists (with the same vertex order) then
835                    // we have two triangles sharing the same but with opposite incompatible orientations.
836                    return Err(TopologyError::BadAdjacentTrianglesOrientation {
837                        edge: edge_key,
838                        triangle1: topology.half_edges[existing as usize].face,
839                        triangle2: fid as u32,
840                    });
841                }
842
843                topology.vertices[idx[k as usize] as usize].half_edge = half_edge_base_id + k;
844            }
845
846            topology.faces.push(TopoFace {
847                half_edge: half_edge_base_id,
848            })
849        }
850
851        // Second, identify twins.
852        for (key, he1) in &half_edge_map {
853            if key.0 < key.1 {
854                // Test, to avoid checking the same pair twice.
855                if let Some(he2) = half_edge_map.get(&(key.1, key.0)) {
856                    topology.half_edges[*he1 as usize].twin = *he2;
857                    topology.half_edges[*he2 as usize].twin = *he1;
858                }
859            }
860        }
861
862        self.topology = Some(topology);
863
864        Ok(())
865    }
866
867    // NOTE: this is private because that calculation is controlled by TriMeshFlags::CONNECTED_COMPONENTS
868    // TODO: we should remove the CONNECTED_COMPONENTS flags and just have this be a free function.
869    // TODO: this should be no_std compatible once ena is or once we have an alternative for it.
870    #[cfg(feature = "std")]
871    fn compute_connected_components(&mut self) {
872        use ena::unify::{InPlaceUnificationTable, UnifyKey};
873
874        #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
875        struct IntKey(u32);
876
877        impl UnifyKey for IntKey {
878            type Value = ();
879            fn index(&self) -> u32 {
880                self.0
881            }
882            fn from_index(u: u32) -> IntKey {
883                IntKey(u)
884            }
885            fn tag() -> &'static str {
886                "IntKey"
887            }
888        }
889
890        let mut ufind: InPlaceUnificationTable<IntKey> = InPlaceUnificationTable::new();
891        let mut face_colors = vec![u32::MAX; self.indices.len()];
892        let mut ranges = vec![0];
893        let mut vertex_to_range = vec![u32::MAX; self.vertices.len()];
894        let mut grouped_faces = vec![u32::MAX; self.indices.len()];
895        let mut vertex_to_key = vec![IntKey(u32::MAX); self.vertices.len()];
896
897        let mut vertex_key = |id: u32, ufind: &mut InPlaceUnificationTable<IntKey>| {
898            if vertex_to_key[id as usize].0 == u32::MAX {
899                let new_key = ufind.new_key(());
900                vertex_to_key[id as usize] = new_key;
901                new_key
902            } else {
903                vertex_to_key[id as usize]
904            }
905        };
906
907        for idx in self.indices() {
908            let keys = idx.map(|i| vertex_key(i, &mut ufind));
909            ufind.union(keys[0], keys[1]);
910            ufind.union(keys[1], keys[2]);
911            ufind.union(keys[2], keys[0]);
912        }
913
914        for (idx, face_color) in self.indices().iter().zip(face_colors.iter_mut()) {
915            debug_assert_eq!(
916                ufind.find(vertex_to_key[idx[0] as usize]),
917                ufind.find(vertex_to_key[idx[1] as usize])
918            );
919            debug_assert_eq!(
920                ufind.find(vertex_to_key[idx[0] as usize]),
921                ufind.find(vertex_to_key[idx[2] as usize])
922            );
923
924            let group_index = ufind.find(vertex_to_key[idx[0] as usize]).0 as usize;
925
926            if vertex_to_range[group_index] == u32::MAX {
927                // Additional range
928                ranges.push(0);
929                vertex_to_range[group_index] = ranges.len() as u32 - 1;
930            }
931
932            let range_id = vertex_to_range[group_index];
933            ranges[range_id as usize] += 1;
934            // NOTE: the range_id points to the range upper bound. The face color is the range lower bound.
935            *face_color = range_id - 1;
936        }
937
938        // Cumulated sum on range indices, to find the first index faces need to be inserted into
939        // for each range.
940        for i in 1..ranges.len() {
941            ranges[i] += ranges[i - 1];
942        }
943
944        debug_assert_eq!(*ranges.last().unwrap(), self.indices().len());
945
946        // Group faces.
947        let mut insertion_in_range_index = ranges.clone();
948        for (face_id, face_color) in face_colors.iter().enumerate() {
949            let insertion_index = &mut insertion_in_range_index[*face_color as usize];
950            grouped_faces[*insertion_index] = face_id as u32;
951            *insertion_index += 1;
952        }
953
954        self.connected_components = Some(TriMeshConnectedComponents {
955            face_colors,
956            grouped_faces,
957            ranges,
958        })
959    }
960
961    #[allow(dead_code)] // Useful for testing.
962    pub(crate) fn assert_half_edge_topology_is_valid(&self) {
963        let topo = self
964            .topology
965            .as_ref()
966            .expect("No topology information found.");
967        assert_eq!(self.vertices.len(), topo.vertices.len());
968        assert_eq!(self.indices.len(), topo.faces.len());
969
970        for (face_id, (face, idx)) in topo.faces.iter().zip(self.indices.iter()).enumerate() {
971            let he0 = topo.half_edges[face.half_edge as usize];
972            assert_eq!(he0.face, face_id as u32);
973            assert_eq!(he0.vertex, idx[0]);
974            let he1 = topo.half_edges[he0.next as usize];
975            assert_eq!(he1.face, face_id as u32);
976            assert_eq!(he1.vertex, idx[1]);
977            let he2 = topo.half_edges[he1.next as usize];
978            assert_eq!(he2.face, face_id as u32);
979            assert_eq!(he2.vertex, idx[2]);
980            assert_eq!(he2.next, face.half_edge);
981        }
982
983        for he in &topo.half_edges {
984            let idx = &self.indices[he.face as usize];
985            assert!(he.vertex == idx[0] || he.vertex == idx[1] || he.vertex == idx[2]);
986        }
987    }
988
989    /// An iterator through all the triangles of this mesh.
990    pub fn triangles(&self) -> impl ExactSizeIterator<Item = Triangle> + '_ {
991        self.indices.iter().map(move |ids| {
992            Triangle::new(
993                self.vertices[ids[0] as usize],
994                self.vertices[ids[1] as usize],
995                self.vertices[ids[2] as usize],
996            )
997        })
998    }
999
1000    #[cfg(feature = "dim3")]
1001    /// Gets the normal of the triangle represented by `feature`.
1002    pub fn feature_normal(&self, feature: FeatureId) -> Option<Unit<Vector<Real>>> {
1003        match feature {
1004            FeatureId::Face(i) => self
1005                .triangle(i % self.num_triangles() as u32)
1006                .feature_normal(FeatureId::Face(0)),
1007            _ => None,
1008        }
1009    }
1010}
1011
1012impl TriMesh {
1013    /// The flags of this triangle mesh.
1014    pub fn flags(&self) -> TriMeshFlags {
1015        self.flags
1016    }
1017
1018    /// Compute the axis-aligned bounding box of this triangle mesh.
1019    pub fn aabb(&self, pos: &Isometry<Real>) -> Aabb {
1020        self.bvh.root_aabb().transform_by(pos)
1021    }
1022
1023    /// Gets the local axis-aligned bounding box of this triangle mesh.
1024    pub fn local_aabb(&self) -> Aabb {
1025        self.bvh.root_aabb()
1026    }
1027
1028    /// The acceleration structure used by this triangle-mesh.
1029    pub fn bvh(&self) -> &Bvh {
1030        &self.bvh
1031    }
1032
1033    /// The number of triangles forming this mesh.
1034    pub fn num_triangles(&self) -> usize {
1035        self.indices.len()
1036    }
1037
1038    /// Does the given feature ID identify a backface of this trimesh?
1039    pub fn is_backface(&self, feature: FeatureId) -> bool {
1040        if let FeatureId::Face(i) = feature {
1041            i >= self.indices.len() as u32
1042        } else {
1043            false
1044        }
1045    }
1046
1047    /// Get the `i`-th triangle of this mesh.
1048    pub fn triangle(&self, i: u32) -> Triangle {
1049        let idx = self.indices[i as usize];
1050        Triangle::new(
1051            self.vertices[idx[0] as usize],
1052            self.vertices[idx[1] as usize],
1053            self.vertices[idx[2] as usize],
1054        )
1055    }
1056
1057    /// Returns the pseudo-normals of one of this mesh’s triangles, if it was computed.
1058    ///
1059    /// This returns `None` if the pseudo-normals of this triangle were not computed.
1060    /// To have its pseudo-normals computed, be sure to set the [`TriMeshFlags`] so that
1061    /// they contain the [`TriMeshFlags::FIX_INTERNAL_EDGES`] flag.
1062    #[cfg(feature = "dim3")]
1063    pub fn triangle_normal_constraints(&self, i: u32) -> Option<TrianglePseudoNormals> {
1064        if self.flags.contains(TriMeshFlags::FIX_INTERNAL_EDGES) {
1065            let triangle = self.triangle(i);
1066            let pseudo_normals = self.pseudo_normals.as_ref()?;
1067            let edges_pseudo_normals = pseudo_normals.edges_pseudo_normal[i as usize];
1068
1069            // TODO: could the pseudo-normal be pre-normalized instead of having to renormalize
1070            //       every time we need them?
1071            Some(TrianglePseudoNormals {
1072                face: triangle.normal()?,
1073                edges: [
1074                    Unit::try_new(edges_pseudo_normals[0], 1.0e-6)?,
1075                    Unit::try_new(edges_pseudo_normals[1], 1.0e-6)?,
1076                    Unit::try_new(edges_pseudo_normals[2], 1.0e-6)?,
1077                ],
1078            })
1079        } else {
1080            None
1081        }
1082    }
1083
1084    #[cfg(feature = "dim2")]
1085    #[doc(hidden)]
1086    pub fn triangle_normal_constraints(&self, _i: u32) -> Option<TrianglePseudoNormals> {
1087        None
1088    }
1089
1090    /// The vertex buffer of this mesh.
1091    pub fn vertices(&self) -> &[Point<Real>] {
1092        &self.vertices
1093    }
1094
1095    /// The index buffer of this mesh.
1096    pub fn indices(&self) -> &[[u32; 3]] {
1097        &self.indices
1098    }
1099
1100    /// Returns the topology information of this trimesh, if it has been computed.
1101    pub fn topology(&self) -> Option<&TriMeshTopology> {
1102        self.topology.as_ref()
1103    }
1104
1105    /// Returns the connected-component information of this trimesh, if it has been computed.
1106    pub fn connected_components(&self) -> Option<&TriMeshConnectedComponents> {
1107        self.connected_components.as_ref()
1108    }
1109
1110    /// Returns the connected-component of this mesh.
1111    ///
1112    /// The connected-components are returned as a set of `TriMesh` build with the given `flags`.
1113    pub fn connected_component_meshes(
1114        &self,
1115        flags: TriMeshFlags,
1116    ) -> Option<Vec<Result<TriMesh, TriMeshBuilderError>>> {
1117        self.connected_components()
1118            .map(|cc| cc.to_meshes(self, flags))
1119    }
1120
1121    /// The pseudo-normals of this triangle mesh, if they have been computed.
1122    #[cfg(feature = "dim3")]
1123    pub fn pseudo_normals(&self) -> Option<&TriMeshPseudoNormals> {
1124        self.pseudo_normals.as_ref()
1125    }
1126
1127    /// The pseudo-normals of this triangle mesh, if they have been computed **and** this mesh was
1128    /// marked as [`TriMeshFlags::ORIENTED`].
1129    #[cfg(feature = "dim3")]
1130    pub fn pseudo_normals_if_oriented(&self) -> Option<&TriMeshPseudoNormals> {
1131        if self.flags.intersects(TriMeshFlags::ORIENTED) {
1132            self.pseudo_normals.as_ref()
1133        } else {
1134            None
1135        }
1136    }
1137}
1138
1139#[cfg(feature = "dim3")]
1140impl From<crate::shape::HeightField> for TriMesh {
1141    fn from(heightfield: crate::shape::HeightField) -> Self {
1142        let (vtx, idx) = heightfield.to_trimesh();
1143        TriMesh::new(vtx, idx).unwrap()
1144    }
1145}
1146
1147#[cfg(feature = "dim3")]
1148impl From<Cuboid> for TriMesh {
1149    fn from(cuboid: Cuboid) -> Self {
1150        let (vtx, idx) = cuboid.to_trimesh();
1151        TriMesh::new(vtx, idx).unwrap()
1152    }
1153}
1154
1155impl CompositeShape for TriMesh {
1156    fn map_part_at(
1157        &self,
1158        i: u32,
1159        f: &mut dyn FnMut(Option<&Isometry<Real>>, &dyn Shape, Option<&dyn NormalConstraints>),
1160    ) {
1161        let tri = self.triangle(i);
1162        let normals = self.triangle_normal_constraints(i);
1163        f(
1164            None,
1165            &tri,
1166            normals.as_ref().map(|n| n as &dyn NormalConstraints),
1167        )
1168    }
1169
1170    fn bvh(&self) -> &Bvh {
1171        &self.bvh
1172    }
1173}
1174
1175impl TypedCompositeShape for TriMesh {
1176    type PartShape = Triangle;
1177    type PartNormalConstraints = TrianglePseudoNormals;
1178
1179    #[inline(always)]
1180    fn map_typed_part_at<T>(
1181        &self,
1182        i: u32,
1183        mut f: impl FnMut(
1184            Option<&Isometry<Real>>,
1185            &Self::PartShape,
1186            Option<&Self::PartNormalConstraints>,
1187        ) -> T,
1188    ) -> Option<T> {
1189        let tri = self.triangle(i);
1190        let pseudo_normals = self.triangle_normal_constraints(i);
1191        Some(f(None, &tri, pseudo_normals.as_ref()))
1192    }
1193
1194    #[inline(always)]
1195    fn map_untyped_part_at<T>(
1196        &self,
1197        i: u32,
1198        mut f: impl FnMut(Option<&Isometry<Real>>, &dyn Shape, Option<&dyn NormalConstraints>) -> T,
1199    ) -> Option<T> {
1200        let tri = self.triangle(i);
1201        let pseudo_normals = self.triangle_normal_constraints(i);
1202        Some(f(
1203            None,
1204            &tri,
1205            pseudo_normals.as_ref().map(|n| n as &dyn NormalConstraints),
1206        ))
1207    }
1208}
1209
1210#[cfg(test)]
1211mod test {
1212    use crate::math::{Real, Vector};
1213    use crate::shape::{Cuboid, TriMesh, TriMeshFlags};
1214
1215    #[test]
1216    fn trimesh_error_empty_indices() {
1217        assert!(
1218            TriMesh::with_flags(vec![], vec![], TriMeshFlags::empty()).is_err(),
1219            "A triangle mesh with no triangles is invalid."
1220        );
1221    }
1222
1223    #[test]
1224    fn connected_components() {
1225        let (vtx, idx) = Cuboid::new(Vector::repeat(0.5)).to_trimesh();
1226
1227        // Push 10 copy of the mesh, each time pushed with an offset.
1228        let mut mesh = TriMesh::new(vtx.clone(), idx.clone()).unwrap();
1229
1230        for i in 1..10 {
1231            let cc_vtx = vtx
1232                .iter()
1233                .map(|pt| pt + Vector::repeat(2.0 * i as Real))
1234                .collect();
1235
1236            let to_append = TriMesh::new(cc_vtx, idx.clone()).unwrap();
1237            mesh.append(&to_append);
1238        }
1239
1240        mesh.set_flags(TriMeshFlags::CONNECTED_COMPONENTS).unwrap();
1241        let connected_components = mesh.connected_components().unwrap();
1242        assert_eq!(connected_components.num_connected_components(), 10);
1243
1244        let cc_meshes = connected_components.to_meshes(&mesh, TriMeshFlags::empty());
1245
1246        for cc in cc_meshes {
1247            let cc = cc.unwrap();
1248            assert_eq!(cc.vertices.len(), vtx.len());
1249            assert_eq!(cc.indices.len(), idx.len());
1250        }
1251    }
1252}