parry2d/shape/trimesh.rs
1use crate::bounding_volume::Aabb;
2#[cfg(feature = "dim3")]
3use crate::math::VectorExt;
4use crate::math::{Pose, Vector};
5use crate::partitioning::{Bvh, BvhBuildStrategy};
6use crate::shape::{FeatureId, Shape, Triangle, TrianglePseudoNormals, TypedCompositeShape};
7use crate::utils::HashablePartialEq;
8use alloc::{vec, vec::Vec};
9use core::fmt;
10#[cfg(feature = "dim3")]
11use {crate::shape::Cuboid, crate::utils::SortedPair};
12
13use {
14 crate::shape::composite_shape::CompositeShape,
15 crate::utils::hashmap::{Entry, HashMap},
16 crate::utils::hashset::HashSet,
17};
18
19#[cfg(feature = "dim2")]
20use crate::transformation::ear_clipping::triangulate_ear_clipping;
21
22use crate::query::details::NormalConstraints;
23
24/// Errors that occur when computing or validating triangle mesh topology.
25///
26/// Triangle mesh topology describes the connectivity and adjacency relationships between
27/// vertices, edges, and triangles. When constructing a mesh with [`TriMeshFlags::HALF_EDGE_TOPOLOGY`],
28/// Parry validates these relationships and returns this error if inconsistencies are found.
29///
30/// # When This Occurs
31///
32/// Topology errors typically occur when:
33/// - The mesh is non-manifold (edges with more than 2 adjacent faces)
34/// - Adjacent triangles have inconsistent winding order
35/// - Triangles have degenerate geometry (duplicate vertices)
36///
37/// [`TriMeshFlags::HALF_EDGE_TOPOLOGY`]: crate::shape::TriMeshFlags::HALF_EDGE_TOPOLOGY
38#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq)]
39pub enum TopologyError {
40 /// A triangle has two or three identical vertices (degenerate triangle).
41 ///
42 /// This error indicates that a triangle in the mesh references the same vertex
43 /// multiple times, creating a degenerate triangle (zero area). For example,
44 /// a triangle with indices `[5, 5, 7]` or `[1, 2, 1]`.
45 ///
46 /// Degenerate triangles cannot be part of a valid mesh topology because they
47 /// don't have proper edges or face normals.
48 ///
49 // /// TODO: figure out why this doc-test fails.
50 // /// # How to Fix
51 // ///
52 // /// ```
53 // /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
54 // /// # use parry3d::shape::{TriMesh, TriMeshFlags};
55 // /// # use parry3d::math::Vector;
56 // /// let vertices = vec![
57 // /// Vector::ZERO,
58 // /// Vector::new(1.0, 0.0, 0.0),
59 // /// Vector::new(0.0, 1.0, 0.0),
60 // /// ];
61 // ///
62 // /// // BAD: Triangle with duplicate vertices
63 // /// let bad_indices = vec![[0, 0, 1]]; // vertex 0 appears twice!
64 // ///
65 // /// let result = TriMesh::with_flags(
66 // /// vertices.clone(),
67 // /// bad_indices,
68 // /// TriMeshFlags::HALF_EDGE_TOPOLOGY
69 // /// );
70 // /// assert!(result.is_err());
71 // ///
72 // /// // GOOD: All three vertices are distinct
73 // /// let good_indices = vec![[0, 1, 2]];
74 // /// let mesh = TriMesh::with_flags(
75 // /// vertices,
76 // /// good_indices,
77 // /// TriMeshFlags::HALF_EDGE_TOPOLOGY
78 // /// ).expect("Valid mesh");
79 // /// # }
80 // /// ```
81 ///
82 /// Alternatively, use [`TriMeshFlags::DELETE_DEGENERATE_TRIANGLES`] to automatically
83 /// remove degenerate triangles:
84 ///
85 /// ```
86 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
87 /// # use parry3d::shape::{TriMesh, TriMeshFlags};
88 /// # use parry3d::math::Vector;
89 /// # let vertices = vec![Vector::ZERO, Vector::new(1.0, 0.0, 0.0), Vector::new(0.0, 1.0, 0.0)];
90 /// # let indices = vec![[0, 0, 1], [0, 1, 2]];
91 /// let flags = TriMeshFlags::HALF_EDGE_TOPOLOGY
92 /// | TriMeshFlags::DELETE_BAD_TOPOLOGY_TRIANGLES;
93 ///
94 /// let mesh = TriMesh::with_flags(vertices, indices, flags)
95 /// .expect("Bad triangles removed");
96 /// # }
97 /// ```
98 #[error("the triangle {0} has at least two identical vertices.")]
99 BadTriangle(u32),
100
101 /// Two adjacent triangles have opposite orientations (inconsistent winding).
102 ///
103 /// For a manifold mesh with consistent normals, adjacent triangles must have
104 /// compatible orientations. If two triangles share an edge, they must traverse
105 /// that edge in opposite directions.
106 ///
107 /// This error reports:
108 /// - `triangle1`, `triangle2`: The indices of the two conflicting triangles
109 /// - `edge`: The shared edge as a pair of vertex indices `(v1, v2)`
110 ///
111 /// # Example of the Problem
112 ///
113 /// ```text
114 /// CORRECT (opposite winding on shared edge):
115 /// Triangle 1: [a, b, c] -> edge (a,b)
116 /// Triangle 2: [b, a, d] -> edge (b,a) ✓ opposite direction
117 ///
118 /// INCORRECT (same winding on shared edge):
119 /// Triangle 1: [a, b, c] -> edge (a,b)
120 /// Triangle 2: [a, b, d] -> edge (a,b) ✗ same direction!
121 /// ```
122 ///
123 // /// TODO: figure out why this doc test fails?
124 // /// # How to Fix
125 // ///
126 // /// You need to reverse the winding order of one of the triangles:
127 // ///
128 // /// ```
129 // /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
130 // /// # use parry3d::shape::{TriMesh, TriMeshFlags};
131 // /// # use parry3d::math::Vector;
132 // /// let vertices = vec![
133 // /// Vector::ZERO, // vertex 0
134 // /// Vector::new(1.0, 0.0, 0.0), // vertex 1
135 // /// Vector::new(0.5, 1.0, 0.0), // vertex 2
136 // /// Vector::new(0.5, -1.0, 0.0), // vertex 3
137 // /// ];
138 // ///
139 // /// // BAD: Both triangles traverse edge (0,1) in the same direction
140 // /// let bad_indices = vec![
141 // /// [0, 1, 2], // edge (0,1)
142 // /// [0, 1, 3], // edge (0,1) - same direction!
143 // /// ];
144 // ///
145 // /// let result = TriMesh::with_flags(
146 // /// vertices.clone(),
147 // /// bad_indices,
148 // /// TriMeshFlags::HALF_EDGE_TOPOLOGY
149 // /// );
150 // /// assert!(result.is_err());
151 // ///
152 // /// // GOOD: Second triangle reversed to [1, 0, 3]
153 // /// let good_indices = vec![
154 // /// [0, 1, 2], // edge (0,1)
155 // /// [1, 0, 3], // edge (1,0) - opposite direction!
156 // /// ];
157 // ///
158 // /// let mesh = TriMesh::with_flags(
159 // /// vertices,
160 // /// good_indices,
161 // /// TriMeshFlags::HALF_EDGE_TOPOLOGY
162 // /// ).expect("Valid mesh");
163 // /// # }
164 // /// ```
165 ///
166 /// # Common Causes
167 ///
168 /// - Mixing clockwise and counter-clockwise triangle definitions
169 /// - Incorrectly constructed mesh from modeling software
170 /// - Manual mesh construction with inconsistent winding
171 /// - Merging separate meshes with different conventions
172 #[error("the triangles {triangle1} and {triangle2} sharing the edge {edge:?} have opposite orientations.")]
173 BadAdjacentTrianglesOrientation {
174 /// The first triangle, with an orientation opposite to the second triangle.
175 triangle1: u32,
176 /// The second triangle, with an orientation opposite to the first triangle.
177 triangle2: u32,
178 /// The edge shared between the two triangles.
179 edge: (u32, u32),
180 },
181}
182
183/// Errors that occur when creating a triangle mesh.
184///
185/// When constructing a [`TriMesh`] using [`TriMesh::new`] or [`TriMesh::with_flags`],
186/// various validation checks are performed. This error type describes what went wrong.
187///
188/// # Common Usage
189///
190/// ```
191/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
192/// # use parry3d::shape::{TriMesh, TriMeshBuilderError, TopologyError};
193/// # use parry3d::math::Vector;
194/// let vertices = vec![Vector::ZERO];
195/// let indices = vec![]; // Empty!
196///
197/// match TriMesh::new(vertices, indices) {
198/// Err(TriMeshBuilderError::EmptyIndices) => {
199/// println!("Cannot create a mesh with no triangles");
200/// }
201/// Err(TriMeshBuilderError::TopologyError(topo_err)) => {
202/// println!("Mesh topology is invalid: {}", topo_err);
203/// }
204/// Ok(mesh) => {
205/// println!("Mesh created successfully");
206/// }
207/// }
208/// # }
209/// ```
210///
211/// [`TriMesh`]: crate::shape::TriMesh
212/// [`TriMesh::new`]: crate::shape::TriMesh::new
213/// [`TriMesh::with_flags`]: crate::shape::TriMesh::with_flags
214#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq)]
215pub enum TriMeshBuilderError {
216 /// The index buffer is empty (no triangles provided).
217 ///
218 /// A triangle mesh must contain at least one triangle. An empty index buffer
219 /// is not valid because there's nothing to render or use for collision detection.
220 ///
221 /// # How to Fix
222 ///
223 /// Ensure your index buffer has at least one triangle:
224 ///
225 /// ```
226 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
227 /// # use parry3d::shape::TriMesh;
228 /// # use parry3d::math::Vector;
229 /// let vertices = vec![
230 /// Vector::ZERO,
231 /// Vector::new(1.0, 0.0, 0.0),
232 /// Vector::new(0.0, 1.0, 0.0),
233 /// ];
234 ///
235 /// // BAD: No triangles
236 /// let empty_indices = vec![];
237 /// assert!(TriMesh::new(vertices.clone(), empty_indices).is_err());
238 ///
239 /// // GOOD: At least one triangle
240 /// let indices = vec![[0, 1, 2]];
241 /// assert!(TriMesh::new(vertices, indices).is_ok());
242 /// # }
243 /// ```
244 #[error("A triangle mesh must contain at least one triangle.")]
245 EmptyIndices,
246
247 /// The mesh topology is invalid.
248 ///
249 /// This wraps a [`TopologyError`] that provides details about the specific
250 /// topology problem. This only occurs when creating a mesh with topology
251 /// validation enabled (e.g., [`TriMeshFlags::HALF_EDGE_TOPOLOGY`]).
252 ///
253 /// See [`TopologyError`] for details on specific topology problems and how
254 /// to fix them.
255 ///
256 /// # Example
257 ///
258 /// ```
259 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
260 /// # use parry3d::shape::{TriMesh, TriMeshFlags, TriMeshBuilderError};
261 /// # use parry3d::math::Vector;
262 /// let vertices = vec![
263 /// Vector::ZERO,
264 /// Vector::new(1.0, 0.0, 0.0),
265 /// Vector::new(0.0, 1.0, 0.0),
266 /// ];
267 ///
268 /// // Triangle with duplicate vertices
269 /// let bad_indices = vec![[0, 0, 1]];
270 ///
271 /// match TriMesh::with_flags(vertices, bad_indices, TriMeshFlags::HALF_EDGE_TOPOLOGY) {
272 /// Err(TriMeshBuilderError::TopologyError(topo_err)) => {
273 /// println!("Topology error: {}", topo_err);
274 /// // Handle the specific topology issue
275 /// }
276 /// _ => {}
277 /// }
278 /// # }
279 /// ```
280 ///
281 /// [`TriMeshFlags::HALF_EDGE_TOPOLOGY`]: crate::shape::TriMeshFlags::HALF_EDGE_TOPOLOGY
282 #[error("Topology Error: {0}")]
283 TopologyError(TopologyError),
284}
285
286/// The set of pseudo-normals of a triangle mesh.
287///
288/// These pseudo-normals are used for the inside-outside test of a
289/// point on the triangle, as described in the paper:
290/// "Signed distance computation using the angle weighted pseudonormal", Baerentzen, et al.
291/// DOI: 10.1109/TVCG.2005.49
292#[derive(Default, Clone)]
293#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
294#[cfg_attr(
295 feature = "rkyv",
296 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
297)]
298#[repr(C)]
299#[cfg(feature = "dim3")]
300pub struct TriMeshPseudoNormals {
301 /// The pseudo-normals of the vertices.
302 pub vertices_pseudo_normal: Vec<Vector>,
303 /// The pseudo-normals of the edges.
304 pub edges_pseudo_normal: Vec<[Vector; 3]>,
305}
306
307/// The connected-components of a triangle mesh.
308#[derive(Debug, Clone)]
309#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
310#[cfg_attr(
311 feature = "rkyv",
312 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
313)]
314#[repr(C)]
315pub struct TriMeshConnectedComponents {
316 /// The `face_colors[i]` gives the connected-component index
317 /// of the i-th face.
318 pub face_colors: Vec<u32>,
319 /// The set of faces grouped by connected components.
320 pub grouped_faces: Vec<u32>,
321 /// The range of connected components. `self.grouped_faces[self.ranges[i]..self.ranges[i + 1]]`
322 /// contains the indices of all the faces part of the i-th connected component.
323 pub ranges: Vec<usize>,
324}
325
326impl TriMeshConnectedComponents {
327 /// The total number of connected components.
328 pub fn num_connected_components(&self) -> usize {
329 self.ranges.len() - 1
330 }
331
332 /// Convert the connected-component description into actual meshes (returned as raw index and
333 /// vertex buffers).
334 ///
335 /// The `mesh` must be the one used to generate `self`, otherwise it might panic or produce an
336 /// unexpected result.
337 pub fn to_mesh_buffers(&self, mesh: &TriMesh) -> Vec<(Vec<Vector>, Vec<[u32; 3]>)> {
338 let mut result = vec![];
339 let mut new_vtx_index: Vec<_> = vec![u32::MAX; mesh.vertices.len()];
340
341 for ranges in self.ranges.windows(2) {
342 let num_faces = ranges[1] - ranges[0];
343
344 if num_faces == 0 {
345 continue;
346 }
347
348 let mut vertices = Vec::with_capacity(num_faces);
349 let mut indices = Vec::with_capacity(num_faces);
350
351 for fid in ranges[0]..ranges[1] {
352 let vids = mesh.indices[self.grouped_faces[fid] as usize];
353 let new_vids = vids.map(|id| {
354 if new_vtx_index[id as usize] == u32::MAX {
355 vertices.push(mesh.vertices[id as usize]);
356 new_vtx_index[id as usize] = vertices.len() as u32 - 1;
357 }
358
359 new_vtx_index[id as usize]
360 });
361 indices.push(new_vids);
362 }
363
364 result.push((vertices, indices));
365 }
366
367 result
368 }
369
370 /// Convert the connected-component description into actual meshes.
371 ///
372 /// The `mesh` must be the one used to generate `self`, otherwise it might panic or produce an
373 /// unexpected result.
374 ///
375 /// All the meshes are constructed with the given `flags`.
376 pub fn to_meshes(
377 &self,
378 mesh: &TriMesh,
379 flags: TriMeshFlags,
380 ) -> Vec<Result<TriMesh, TriMeshBuilderError>> {
381 self.to_mesh_buffers(mesh)
382 .into_iter()
383 .map(|(vtx, idx)| TriMesh::with_flags(vtx, idx, flags))
384 .collect()
385 }
386}
387
388/// A vertex of a triangle-mesh’s half-edge topology.
389#[derive(Clone, Copy, Debug)]
390#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
391#[cfg_attr(
392 feature = "rkyv",
393 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
394)]
395#[repr(C)]
396pub struct TopoVertex {
397 /// One of the half-edge with this vertex as endpoint.
398 pub half_edge: u32,
399}
400
401/// A face of a triangle-mesh’s half-edge topology.
402#[derive(Clone, Copy, Debug)]
403#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
404#[cfg_attr(
405 feature = "rkyv",
406 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
407)]
408#[repr(C)]
409pub struct TopoFace {
410 /// The half-edge adjacent to this face, with a starting point equal
411 /// to the first point of this face.
412 pub half_edge: u32,
413}
414
415/// A half-edge of a triangle-mesh’s half-edge topology.
416#[derive(Clone, Copy, Debug)]
417#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
418#[cfg_attr(
419 feature = "rkyv",
420 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
421)]
422#[repr(C)]
423pub struct TopoHalfEdge {
424 /// The next half-edge.
425 pub next: u32,
426 /// This half-edge twin on the adjacent triangle.
427 ///
428 /// This is `u32::MAX` if there is no twin.
429 pub twin: u32,
430 /// The first vertex of this edge.
431 pub vertex: u32,
432 /// The face associated to this half-edge.
433 pub face: u32,
434}
435
436/// The half-edge topology information of a triangle mesh.
437#[derive(Default, Clone)]
438#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
439#[cfg_attr(
440 feature = "rkyv",
441 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
442)]
443#[repr(C)]
444pub struct TriMeshTopology {
445 /// The vertices of this half-edge representation.
446 pub vertices: Vec<TopoVertex>,
447 /// The faces of this half-edge representation.
448 pub faces: Vec<TopoFace>,
449 /// The half-edges of this half-edge representation.
450 pub half_edges: Vec<TopoHalfEdge>,
451}
452
453#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
454#[cfg_attr(
455 feature = "rkyv",
456 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
457)]
458#[repr(C)]
459#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
460/// Controls how a [`TriMesh`] should be loaded.
461pub struct TriMeshFlags(u16);
462
463bitflags::bitflags! {
464 impl TriMeshFlags: u16 {
465 /// If set, the half-edge topology of the trimesh will be computed if possible.
466 const HALF_EDGE_TOPOLOGY = 1;
467 /// If set, the connected components of the trimesh will be computed.
468 const CONNECTED_COMPONENTS = 1 << 1;
469 /// If set, any triangle that results in a failing half-hedge topology computation will be deleted.
470 const DELETE_BAD_TOPOLOGY_TRIANGLES = 1 << 2;
471 /// If set, the trimesh will be assumed to be oriented (with outward normals).
472 ///
473 /// The pseudo-normals of its vertices and edges will be computed.
474 const ORIENTED = 1 << 3;
475 /// If set, the duplicate vertices of the trimesh will be merged.
476 ///
477 /// Two vertices with the exact same coordinates will share the same entry on the
478 /// vertex buffer and the index buffer is adjusted accordingly.
479 const MERGE_DUPLICATE_VERTICES = 1 << 4;
480 /// If set, the triangles sharing two vertices with identical index values will be removed.
481 ///
482 /// Because of the way it is currently implemented, this methods implies that duplicate
483 /// vertices will be merged. It will no longer be the case in the future once we decouple
484 /// the computations.
485 const DELETE_DEGENERATE_TRIANGLES = 1 << 5;
486 /// If set, two triangles sharing three vertices with identical index values (in any order)
487 /// will be removed.
488 ///
489 /// Because of the way it is currently implemented, this methods implies that duplicate
490 /// vertices will be merged. It will no longer be the case in the future once we decouple
491 /// the computations.
492 const DELETE_DUPLICATE_TRIANGLES = 1 << 6;
493 /// If set, a special treatment will be applied to contact manifold calculation to eliminate
494 /// or fix contacts normals that could lead to incorrect bumps in physics simulation
495 /// (especially on flat surfaces).
496 ///
497 /// This is achieved by taking into account adjacent triangle normals when computing contact
498 /// points for a given triangle.
499 const FIX_INTERNAL_EDGES = (1 << 7) | Self::MERGE_DUPLICATE_VERTICES.bits();
500 }
501}
502
503#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
504#[cfg_attr(
505 feature = "rkyv",
506 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
507)]
508#[repr(C)]
509#[derive(Clone)]
510/// A triangle mesh.
511pub struct TriMesh {
512 bvh: Bvh,
513 vertices: Vec<Vector>,
514 indices: Vec<[u32; 3]>,
515 #[cfg(feature = "dim3")]
516 pub(crate) pseudo_normals: Option<TriMeshPseudoNormals>,
517 topology: Option<TriMeshTopology>,
518 connected_components: Option<TriMeshConnectedComponents>,
519 flags: TriMeshFlags,
520}
521
522// NOTE: can't be derived because of the `Bvh` and topology fields; print a
523// summary useful for quickly validating the mesh instead.
524impl fmt::Debug for TriMesh {
525 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
526 let mut dbg = f.debug_struct("TriMesh");
527 let dbg = dbg
528 .field("num_vertices", &self.vertices.len())
529 .field("num_triangles", &self.indices.len())
530 .field("local_aabb", &self.local_aabb())
531 .field("flags", &self.flags);
532
533 #[cfg(feature = "dim3")]
534 let dbg = dbg.field("has_pseudo_normals", &self.pseudo_normals.is_some());
535
536 dbg.field("has_topology", &self.topology.is_some())
537 .field(
538 "has_connected_components",
539 &self.connected_components.is_some(),
540 )
541 .finish_non_exhaustive()
542 }
543}
544
545impl TriMesh {
546 /// Creates a new triangle mesh from a vertex buffer and an index buffer.
547 ///
548 /// This is the most common way to construct a `TriMesh`. The mesh is created with
549 /// default settings (no topology computation, no pseudo-normals, etc.). For more
550 /// control over these optional features, use [`TriMesh::with_flags`] instead.
551 ///
552 /// # Arguments
553 ///
554 /// * `vertices` - A vector of 3D points representing the mesh vertices
555 /// * `indices` - A vector of triangles, where each triangle is represented by
556 /// three indices into the vertex buffer. Indices should be in counter-clockwise
557 /// order for outward-facing normals.
558 ///
559 /// # Returns
560 ///
561 /// * `Ok(TriMesh)` - If the mesh was successfully created
562 /// * `Err(TriMeshBuilderError)` - If the mesh is invalid (e.g., empty index buffer)
563 ///
564 /// # Errors
565 ///
566 /// This function returns an error if:
567 /// - The index buffer is empty (at least one triangle is required)
568 ///
569 /// # Performance
570 ///
571 /// This function builds a BVH (Bounding Volume Hierarchy) acceleration structure
572 /// for the mesh, which takes O(n log n) time where n is the number of triangles.
573 ///
574 /// # Example
575 ///
576 /// ```
577 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
578 /// use parry3d::shape::TriMesh;
579 /// use parry3d::math::Vector;
580 ///
581 /// // Create a simple triangle mesh (a single triangle)
582 /// let vertices = vec![
583 /// Vector::ZERO,
584 /// Vector::new(1.0, 0.0, 0.0),
585 /// Vector::new(0.0, 1.0, 0.0),
586 /// ];
587 /// let indices = vec![[0, 1, 2]];
588 ///
589 /// let trimesh = TriMesh::new(vertices, indices).expect("Invalid mesh");
590 /// assert_eq!(trimesh.num_triangles(), 1);
591 /// # }
592 /// ```
593 ///
594 /// ```
595 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
596 /// use parry2d::shape::TriMesh;
597 /// use parry2d::math::Vector;
598 ///
599 /// // Create a quad (two triangles) in 2D
600 /// let vertices = vec![
601 /// Vector::ZERO, // bottom-left
602 /// Vector::new(1.0, 0.0), // bottom-right
603 /// Vector::new(1.0, 1.0), // top-right
604 /// Vector::new(0.0, 1.0), // top-left
605 /// ];
606 /// let indices = vec![
607 /// [0, 1, 2], // first triangle
608 /// [0, 2, 3], // second triangle
609 /// ];
610 ///
611 /// let quad = TriMesh::new(vertices, indices).unwrap();
612 /// assert_eq!(quad.num_triangles(), 2);
613 /// assert_eq!(quad.vertices().len(), 4);
614 /// # }
615 /// ```
616 pub fn new(vertices: Vec<Vector>, indices: Vec<[u32; 3]>) -> Result<Self, TriMeshBuilderError> {
617 Self::with_flags(vertices, indices, TriMeshFlags::empty())
618 }
619
620 /// Creates a new triangle mesh from a vertex buffer and an index buffer, and flags controlling optional properties.
621 ///
622 /// This is the most flexible way to create a `TriMesh`, allowing you to specify exactly
623 /// which optional features should be computed. Use this when you need:
624 /// - Half-edge topology for adjacency information
625 /// - Connected components analysis
626 /// - Pseudo-normals for robust inside/outside tests
627 /// - Automatic merging of duplicate vertices
628 /// - Removal of degenerate or duplicate triangles
629 ///
630 /// # Arguments
631 ///
632 /// * `vertices` - A vector of 3D points representing the mesh vertices
633 /// * `indices` - A vector of triangles, where each triangle is represented by
634 /// three indices into the vertex buffer
635 /// * `flags` - A combination of [`TriMeshFlags`] controlling which optional features to compute
636 ///
637 /// # Returns
638 ///
639 /// * `Ok(TriMesh)` - If the mesh was successfully created
640 /// * `Err(TriMeshBuilderError)` - If the mesh is invalid or topology computation failed
641 ///
642 /// # Errors
643 ///
644 /// This function returns an error if:
645 /// - The index buffer is empty
646 /// - [`TriMeshFlags::HALF_EDGE_TOPOLOGY`] is set but the topology is invalid
647 /// (e.g., non-manifold edges or inconsistent orientations)
648 ///
649 /// # Performance
650 ///
651 /// - Base construction: O(n log n) for BVH building
652 /// - `HALF_EDGE_TOPOLOGY`: O(n) where n is the number of triangles
653 /// - `CONNECTED_COMPONENTS`: O(n) with union-find
654 /// - `ORIENTED` or `FIX_INTERNAL_EDGES`: O(n) for pseudo-normal computation
655 /// - `MERGE_DUPLICATE_VERTICES`: O(n) with hash map lookups
656 ///
657 /// # Example
658 ///
659 /// ```
660 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
661 /// use parry3d::shape::{TriMesh, TriMeshFlags};
662 /// use parry3d::math::Vector;
663 ///
664 /// // Create vertices for a simple mesh
665 /// let vertices = vec![
666 /// Vector::ZERO,
667 /// Vector::new(1.0, 0.0, 0.0),
668 /// Vector::new(0.0, 1.0, 0.0),
669 /// Vector::new(1.0, 1.0, 0.0),
670 /// ];
671 /// let indices = vec![[0, 1, 2], [1, 3, 2]];
672 ///
673 /// // Create a mesh with half-edge topology
674 /// let flags = TriMeshFlags::HALF_EDGE_TOPOLOGY;
675 /// let mesh = TriMesh::with_flags(vertices.clone(), indices.clone(), flags)
676 /// .expect("Failed to create mesh");
677 ///
678 /// // The topology information is now available
679 /// assert!(mesh.topology().is_some());
680 /// # }
681 /// ```
682 ///
683 /// ```
684 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
685 /// use parry3d::shape::{TriMesh, TriMeshFlags};
686 /// use parry3d::math::Vector;
687 ///
688 /// # let vertices = vec![
689 /// # Vector::ZERO,
690 /// # Vector::new(1.0, 0.0, 0.0),
691 /// # Vector::new(0.0, 1.0, 0.0),
692 /// # ];
693 /// # let indices = vec![[0, 1, 2]];
694 /// // Combine multiple flags for advanced features
695 /// let flags = TriMeshFlags::HALF_EDGE_TOPOLOGY
696 /// | TriMeshFlags::MERGE_DUPLICATE_VERTICES
697 /// | TriMeshFlags::DELETE_DEGENERATE_TRIANGLES;
698 ///
699 /// let mesh = TriMesh::with_flags(vertices, indices, flags)
700 /// .expect("Failed to create mesh");
701 /// # }
702 /// ```
703 ///
704 /// ```
705 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
706 /// use parry3d::shape::{TriMesh, TriMeshFlags};
707 /// use parry3d::math::Vector;
708 ///
709 /// # let vertices = vec![
710 /// # Vector::ZERO,
711 /// # Vector::new(1.0, 0.0, 0.0),
712 /// # Vector::new(0.0, 1.0, 0.0),
713 /// # ];
714 /// # let indices = vec![[0, 1, 2]];
715 /// // For robust point containment tests in 3D
716 /// let flags = TriMeshFlags::ORIENTED;
717 /// let mesh = TriMesh::with_flags(vertices, indices, flags)
718 /// .expect("Failed to create mesh");
719 ///
720 /// // Pseudo-normals are now computed for accurate inside/outside tests
721 /// assert!(mesh.pseudo_normals().is_some());
722 /// # }
723 /// ```
724 pub fn with_flags(
725 vertices: Vec<Vector>,
726 indices: Vec<[u32; 3]>,
727 flags: TriMeshFlags,
728 ) -> Result<Self, TriMeshBuilderError> {
729 if indices.is_empty() {
730 return Err(TriMeshBuilderError::EmptyIndices);
731 }
732
733 let mut result = Self {
734 bvh: Bvh::new(),
735 vertices,
736 indices,
737 #[cfg(feature = "dim3")]
738 pseudo_normals: None,
739 topology: None,
740 connected_components: None,
741 flags: TriMeshFlags::empty(),
742 };
743
744 let _ = result.set_flags(flags);
745
746 if result.bvh.is_empty() {
747 // The BVH hasn’t been computed by `.set_flags`.
748 result.rebuild_bvh();
749 }
750
751 Ok(result)
752 }
753
754 /// Sets the flags of this triangle mesh, controlling its optional associated data.
755 pub fn set_flags(&mut self, flags: TriMeshFlags) -> Result<(), TopologyError> {
756 let mut result = Ok(());
757 let prev_indices_len = self.indices.len();
758
759 if !flags.contains(TriMeshFlags::HALF_EDGE_TOPOLOGY) {
760 self.topology = None;
761 }
762
763 #[cfg(feature = "dim3")]
764 if !flags.intersects(TriMeshFlags::ORIENTED | TriMeshFlags::FIX_INTERNAL_EDGES) {
765 self.pseudo_normals = None;
766 }
767
768 if !flags.contains(TriMeshFlags::CONNECTED_COMPONENTS) {
769 self.connected_components = None;
770 }
771
772 let difference = flags & !self.flags;
773
774 if difference.intersects(
775 TriMeshFlags::MERGE_DUPLICATE_VERTICES
776 | TriMeshFlags::DELETE_DEGENERATE_TRIANGLES
777 | TriMeshFlags::DELETE_DUPLICATE_TRIANGLES,
778 ) {
779 self.merge_duplicate_vertices(
780 flags.contains(TriMeshFlags::DELETE_DEGENERATE_TRIANGLES),
781 flags.contains(TriMeshFlags::DELETE_DUPLICATE_TRIANGLES),
782 )
783 }
784
785 if difference.intersects(
786 TriMeshFlags::HALF_EDGE_TOPOLOGY | TriMeshFlags::DELETE_BAD_TOPOLOGY_TRIANGLES,
787 ) {
788 result =
789 self.compute_topology(flags.contains(TriMeshFlags::DELETE_BAD_TOPOLOGY_TRIANGLES));
790 }
791
792 #[cfg(feature = "std")]
793 if difference.intersects(TriMeshFlags::CONNECTED_COMPONENTS) {
794 self.compute_connected_components();
795 }
796
797 #[cfg(feature = "dim3")]
798 if difference.intersects(TriMeshFlags::ORIENTED | TriMeshFlags::FIX_INTERNAL_EDGES) {
799 self.compute_pseudo_normals();
800 }
801
802 if prev_indices_len != self.indices.len() {
803 self.rebuild_bvh();
804 }
805
806 self.flags = flags;
807 result
808 }
809
810 // TODO: support a crate like get_size2 (will require support on nalgebra too)?
811 /// An approximation of the memory usage (in bytes) for this struct plus
812 /// the memory it allocates dynamically.
813 pub fn total_memory_size(&self) -> usize {
814 size_of::<Self>() + self.heap_memory_size()
815 }
816
817 /// An approximation of the memory dynamically-allocated by this struct.
818 pub fn heap_memory_size(&self) -> usize {
819 // NOTE: if a new field is added to `Self`, adjust this function result.
820 let Self {
821 bvh,
822 vertices,
823 indices,
824 topology,
825 connected_components,
826 flags: _,
827 #[cfg(feature = "dim3")]
828 pseudo_normals,
829 } = self;
830 let sz_bvh = bvh.heap_memory_size();
831 let sz_vertices = vertices.capacity() * size_of::<Vector>();
832 let sz_indices = indices.capacity() * size_of::<[u32; 3]>();
833 #[cfg(feature = "dim3")]
834 let sz_pseudo_normals = pseudo_normals
835 .as_ref()
836 .map(|pn| {
837 pn.vertices_pseudo_normal.capacity() * size_of::<Vector>()
838 + pn.edges_pseudo_normal.capacity() * size_of::<[Vector; 3]>()
839 })
840 .unwrap_or(0);
841 #[cfg(feature = "dim2")]
842 let sz_pseudo_normals = 0;
843 let sz_topology = topology
844 .as_ref()
845 .map(|t| {
846 t.vertices.capacity() * size_of::<TopoVertex>()
847 + t.faces.capacity() * size_of::<TopoFace>()
848 + t.half_edges.capacity() * size_of::<TopoHalfEdge>()
849 })
850 .unwrap_or(0);
851 let sz_connected_components = connected_components
852 .as_ref()
853 .map(|c| {
854 c.face_colors.capacity() * size_of::<u32>()
855 + c.grouped_faces.capacity() * size_of::<f32>()
856 + c.ranges.capacity() * size_of::<usize>()
857 })
858 .unwrap_or(0);
859
860 sz_bvh
861 + sz_vertices
862 + sz_indices
863 + sz_pseudo_normals
864 + sz_topology
865 + sz_connected_components
866 }
867
868 /// Transforms in-place the vertices of this triangle mesh.
869 ///
870 /// Applies a rigid transformation (rotation and translation) to all vertices
871 /// of the mesh. This is useful for positioning or orienting a mesh in world space.
872 /// The transformation also updates the BVH and pseudo-normals (if present).
873 ///
874 /// # Arguments
875 ///
876 /// * `transform` - The isometry (rigid transformation) to apply to all vertices
877 ///
878 /// # Performance
879 ///
880 /// This operation is O(n) for transforming vertices, plus O(n log n) for
881 /// rebuilding the BVH, where n is the number of triangles.
882 ///
883 /// # Example
884 ///
885 /// ```
886 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
887 /// use parry3d::shape::TriMesh;
888 /// use parry3d::math::{Vector, Pose};
889 ///
890 /// let vertices = vec![
891 /// Vector::ZERO,
892 /// Vector::new(1.0, 0.0, 0.0),
893 /// Vector::new(0.0, 1.0, 0.0),
894 /// ];
895 /// let indices = vec![[0, 1, 2]];
896 /// let mut mesh = TriMesh::new(vertices, indices).unwrap();
897 ///
898 /// // Translate the mesh by (10, 0, 0)
899 /// let transform = Pose::translation(10.0, 0.0, 0.0);
900 /// mesh.transform_vertices(&transform);
901 ///
902 /// // All vertices are now shifted
903 /// assert_eq!(mesh.vertices()[0], Vector::new(10.0, 0.0, 0.0));
904 /// assert_eq!(mesh.vertices()[1], Vector::new(11.0, 0.0, 0.0));
905 /// assert_eq!(mesh.vertices()[2], Vector::new(10.0, 1.0, 0.0));
906 /// # }
907 /// ```
908 pub fn transform_vertices(&mut self, transform: &Pose) {
909 self.vertices
910 .iter_mut()
911 .for_each(|pt| *pt = transform * *pt);
912 self.rebuild_bvh();
913
914 // The pseudo-normals must be rotated too.
915 #[cfg(feature = "dim3")]
916 if let Some(pseudo_normals) = &mut self.pseudo_normals {
917 pseudo_normals
918 .vertices_pseudo_normal
919 .iter_mut()
920 .for_each(|n| *n = transform.rotation * *n);
921 pseudo_normals.edges_pseudo_normal.iter_mut().for_each(|n| {
922 n[0] = transform.rotation * n[0];
923 n[1] = transform.rotation * n[1];
924 n[2] = transform.rotation * n[2];
925 });
926 }
927 }
928
929 /// Returns a scaled version of this triangle mesh.
930 ///
931 /// Creates a new mesh with all vertices scaled by the given per-axis scale factors.
932 /// Unlike rigid transformations, scaling can change the shape of the mesh (when
933 /// scale factors differ between axes).
934 ///
935 /// The scaling also updates pseudo-normals (if present) to remain valid after
936 /// the transformation, and efficiently scales the BVH structure.
937 ///
938 /// # Arguments
939 ///
940 /// * `scale` - The scale factors for each axis (x, y, z in 3D)
941 ///
942 /// # Returns
943 ///
944 /// A new `TriMesh` with scaled vertices
945 ///
946 /// # Example
947 ///
948 /// ```
949 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
950 /// use parry3d::shape::TriMesh;
951 /// use parry3d::math::Vector;
952 ///
953 /// let vertices = vec![
954 /// Vector::ZERO,
955 /// Vector::new(1.0, 0.0, 0.0),
956 /// Vector::new(0.0, 1.0, 0.0),
957 /// ];
958 /// let indices = vec![[0, 1, 2]];
959 /// let mesh = TriMesh::new(vertices, indices).unwrap();
960 ///
961 /// // Uniform scaling: double all dimensions
962 /// let scaled = mesh.clone().scaled(Vector::new(2.0, 2.0, 2.0));
963 /// assert_eq!(scaled.vertices()[1], Vector::new(2.0, 0.0, 0.0));
964 ///
965 /// // Non-uniform scaling: stretch along X axis
966 /// let stretched = mesh.scaled(Vector::new(3.0, 1.0, 1.0));
967 /// assert_eq!(stretched.vertices()[1], Vector::new(3.0, 0.0, 0.0));
968 /// assert_eq!(stretched.vertices()[2], Vector::new(0.0, 1.0, 0.0));
969 /// # }
970 /// ```
971 pub fn scaled(mut self, scale: Vector) -> Self {
972 self.vertices.iter_mut().for_each(|pt| *pt *= scale);
973
974 #[cfg(feature = "dim3")]
975 if let Some(pn) = &mut self.pseudo_normals {
976 pn.vertices_pseudo_normal.iter_mut().for_each(|n| {
977 *n *= scale;
978 *n = n.normalize_or(*n);
979 });
980 pn.edges_pseudo_normal.iter_mut().for_each(|n| {
981 n[0] *= scale;
982 n[1] *= scale;
983 n[2] *= scale;
984
985 n[0] = n[0].normalize_or(n[0]);
986 n[1] = n[1].normalize_or(n[1]);
987 n[2] = n[2].normalize_or(n[2]);
988 });
989 }
990
991 let mut bvh = self.bvh.clone();
992 bvh.scale(scale);
993
994 Self {
995 bvh,
996 vertices: self.vertices,
997 indices: self.indices,
998 #[cfg(feature = "dim3")]
999 pseudo_normals: self.pseudo_normals,
1000 topology: self.topology,
1001 connected_components: self.connected_components,
1002 flags: self.flags,
1003 }
1004 }
1005
1006 /// Appends a second triangle mesh to this triangle mesh.
1007 ///
1008 /// This combines two meshes into one by adding all vertices and triangles from
1009 /// the `rhs` mesh to this mesh. The vertex indices in the appended triangles
1010 /// are automatically adjusted to reference the correct vertices in the combined
1011 /// vertex buffer.
1012 ///
1013 /// After appending, all optional features (topology, pseudo-normals, etc.) are
1014 /// recomputed according to this mesh's current flags.
1015 ///
1016 /// # Arguments
1017 ///
1018 /// * `rhs` - The mesh to append to this mesh
1019 ///
1020 /// # Performance
1021 ///
1022 /// This operation rebuilds the entire mesh with all its features, which can be
1023 /// expensive for large meshes. Time complexity is O(n log n) where n is the
1024 /// total number of triangles after appending.
1025 ///
1026 /// # Example
1027 ///
1028 /// ```
1029 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1030 /// use parry3d::shape::TriMesh;
1031 /// use parry3d::math::Vector;
1032 ///
1033 /// // Create first mesh (a triangle)
1034 /// let vertices1 = vec![
1035 /// Vector::ZERO,
1036 /// Vector::new(1.0, 0.0, 0.0),
1037 /// Vector::new(0.0, 1.0, 0.0),
1038 /// ];
1039 /// let indices1 = vec![[0, 1, 2]];
1040 /// let mut mesh1 = TriMesh::new(vertices1, indices1).unwrap();
1041 ///
1042 /// // Create second mesh (another triangle, offset in space)
1043 /// let vertices2 = vec![
1044 /// Vector::new(2.0, 0.0, 0.0),
1045 /// Vector::new(3.0, 0.0, 0.0),
1046 /// Vector::new(2.0, 1.0, 0.0),
1047 /// ];
1048 /// let indices2 = vec![[0, 1, 2]];
1049 /// let mesh2 = TriMesh::new(vertices2, indices2).unwrap();
1050 ///
1051 /// // Append second mesh to first
1052 /// mesh1.append(&mesh2);
1053 ///
1054 /// assert_eq!(mesh1.num_triangles(), 2);
1055 /// assert_eq!(mesh1.vertices().len(), 6);
1056 /// # }
1057 /// ```
1058 pub fn append(&mut self, rhs: &TriMesh) {
1059 let base_id = self.vertices.len() as u32;
1060 self.vertices.extend_from_slice(rhs.vertices());
1061 self.indices.extend(
1062 rhs.indices()
1063 .iter()
1064 .map(|idx| [idx[0] + base_id, idx[1] + base_id, idx[2] + base_id]),
1065 );
1066
1067 let vertices = core::mem::take(&mut self.vertices);
1068 let indices = core::mem::take(&mut self.indices);
1069 *self = TriMesh::with_flags(vertices, indices, self.flags).unwrap();
1070 }
1071
1072 /// Create a `TriMesh` from a set of points assumed to describe a counter-clockwise non-convex polygon.
1073 ///
1074 /// This function triangulates a 2D polygon using ear clipping algorithm. The polygon
1075 /// can be convex or concave, but must be simple (no self-intersections) and have
1076 /// counter-clockwise winding order.
1077 ///
1078 /// # Arguments
1079 ///
1080 /// * `vertices` - The vertices of the polygon in counter-clockwise order
1081 ///
1082 /// # Returns
1083 ///
1084 /// * `Some(TriMesh)` - If triangulation succeeded
1085 /// * `None` - If the polygon is invalid (self-intersecting, degenerate, etc.)
1086 ///
1087 /// # Requirements
1088 ///
1089 /// - Polygon must be simple (no self-intersections)
1090 /// - Vertices must be in counter-clockwise order
1091 /// - Polygon must have non-zero area
1092 /// - Only available in 2D (`dim2` feature)
1093 ///
1094 /// # Performance
1095 ///
1096 /// Ear clipping has O(n²) time complexity in the worst case, where n is the
1097 /// number of vertices.
1098 ///
1099 /// # Example
1100 ///
1101 /// ```
1102 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
1103 /// use parry2d::shape::TriMesh;
1104 /// use parry2d::math::Vector;
1105 ///
1106 /// // Create a simple concave polygon (L-shape)
1107 /// let vertices = vec![
1108 /// Vector::ZERO,
1109 /// Vector::new(2.0, 0.0),
1110 /// Vector::new(2.0, 1.0),
1111 /// Vector::new(1.0, 1.0),
1112 /// Vector::new(1.0, 2.0),
1113 /// Vector::new(0.0, 2.0),
1114 /// ];
1115 ///
1116 /// let mesh = TriMesh::from_polygon(vertices)
1117 /// .expect("Failed to triangulate polygon");
1118 ///
1119 /// // The polygon has been triangulated
1120 /// assert!(mesh.num_triangles() > 0);
1121 /// # }
1122 /// ```
1123 #[cfg(feature = "dim2")]
1124 pub fn from_polygon(vertices: Vec<Vector>) -> Option<Self> {
1125 triangulate_ear_clipping(&vertices).map(|indices| Self::new(vertices, indices).unwrap())
1126 }
1127
1128 /// A flat view of the index buffer of this mesh.
1129 ///
1130 /// Returns the triangle indices as a flat array of `u32` values, where every
1131 /// three consecutive values form a triangle. This is useful for interfacing
1132 /// with graphics APIs that expect flat index buffers.
1133 ///
1134 /// # Example
1135 ///
1136 /// ```
1137 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1138 /// use parry3d::shape::TriMesh;
1139 /// use parry3d::math::Vector;
1140 ///
1141 /// let vertices = vec![
1142 /// Vector::ZERO,
1143 /// Vector::new(1.0, 0.0, 0.0),
1144 /// Vector::new(0.0, 1.0, 0.0),
1145 /// Vector::new(1.0, 1.0, 0.0),
1146 /// ];
1147 /// let indices = vec![[0, 1, 2], [1, 3, 2]];
1148 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1149 ///
1150 /// let flat = mesh.flat_indices();
1151 /// assert_eq!(flat, &[0, 1, 2, 1, 3, 2]);
1152 /// assert_eq!(flat.len(), mesh.num_triangles() * 3);
1153 /// # }
1154 /// ```
1155 pub fn flat_indices(&self) -> &[u32] {
1156 unsafe {
1157 let len = self.indices.len() * 3;
1158 let data = self.indices.as_ptr() as *const u32;
1159 core::slice::from_raw_parts(data, len)
1160 }
1161 }
1162
1163 fn rebuild_bvh(&mut self) {
1164 let leaves = self.indices.iter().enumerate().map(|(i, idx)| {
1165 let aabb = Triangle::new(
1166 self.vertices[idx[0] as usize],
1167 self.vertices[idx[1] as usize],
1168 self.vertices[idx[2] as usize],
1169 )
1170 .local_aabb();
1171 (i, aabb)
1172 });
1173
1174 self.bvh = Bvh::from_iter(BvhBuildStrategy::Binned, leaves)
1175 }
1176
1177 /// Reverse the orientation of the triangle mesh.
1178 ///
1179 /// This flips the winding order of all triangles, effectively turning the mesh
1180 /// "inside-out". If triangles had counter-clockwise winding (outward normals),
1181 /// they will have clockwise winding (inward normals) after this operation.
1182 ///
1183 /// This is useful when:
1184 /// - A mesh was imported with incorrect orientation
1185 /// - You need to flip normals for rendering or physics
1186 /// - Creating the "back side" of a mesh
1187 ///
1188 /// # Performance
1189 ///
1190 /// This operation modifies triangles in-place (O(n)), but recomputes topology
1191 /// and pseudo-normals if they were present, which can be expensive for large meshes.
1192 ///
1193 /// # Example
1194 ///
1195 /// ```
1196 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1197 /// use parry3d::shape::TriMesh;
1198 /// use parry3d::math::Vector;
1199 ///
1200 /// let vertices = vec![
1201 /// Vector::ZERO,
1202 /// Vector::new(1.0, 0.0, 0.0),
1203 /// Vector::new(0.0, 1.0, 0.0),
1204 /// ];
1205 /// let indices = vec![[0, 1, 2]];
1206 ///
1207 /// let mut mesh = TriMesh::new(vertices, indices).unwrap();
1208 /// let original_triangle = mesh.triangle(0);
1209 ///
1210 /// // Reverse the mesh orientation
1211 /// mesh.reverse();
1212 ///
1213 /// let reversed_triangle = mesh.triangle(0);
1214 ///
1215 /// // The first two vertices are swapped
1216 /// assert_eq!(original_triangle.a, reversed_triangle.b);
1217 /// assert_eq!(original_triangle.b, reversed_triangle.a);
1218 /// assert_eq!(original_triangle.c, reversed_triangle.c);
1219 /// # }
1220 /// ```
1221 pub fn reverse(&mut self) {
1222 self.indices.iter_mut().for_each(|idx| idx.swap(0, 1));
1223
1224 // NOTE: the BVH, and connected components are not changed by this operation.
1225 // The pseudo-normals just have to be flipped.
1226 // The topology must be recomputed.
1227
1228 #[cfg(feature = "dim3")]
1229 if let Some(pseudo_normals) = &mut self.pseudo_normals {
1230 for n in &mut pseudo_normals.vertices_pseudo_normal {
1231 *n = -*n;
1232 }
1233
1234 for n in pseudo_normals.edges_pseudo_normal.iter_mut() {
1235 n[0] = -n[0];
1236 n[1] = -n[1];
1237 n[2] = -n[2];
1238 }
1239 }
1240
1241 if self.flags.contains(TriMeshFlags::HALF_EDGE_TOPOLOGY) {
1242 // TODO: this could be done more efficiently.
1243 let _ = self.compute_topology(false);
1244 }
1245 }
1246
1247 /// Merge all duplicate vertices and adjust the index buffer accordingly.
1248 ///
1249 /// If `delete_degenerate_triangles` is set to true, any triangle with two
1250 /// identical vertices will be removed.
1251 ///
1252 /// This is typically used to recover a vertex buffer from which we can deduce
1253 /// adjacency information. between triangles by observing how the vertices are
1254 /// shared by triangles based on the index buffer.
1255 fn merge_duplicate_vertices(
1256 &mut self,
1257 delete_degenerate_triangles: bool,
1258 delete_duplicate_triangles: bool,
1259 ) {
1260 let mut vtx_to_id = HashMap::default();
1261 let mut new_vertices = Vec::with_capacity(self.vertices.len());
1262 let mut new_indices = Vec::with_capacity(self.indices.len());
1263 let mut triangle_set = HashSet::default();
1264
1265 fn resolve_coord_id(
1266 coord: Vector,
1267 vtx_to_id: &mut HashMap<HashablePartialEq<Vector>, u32>,
1268 new_vertices: &mut Vec<Vector>,
1269 ) -> u32 {
1270 let key = HashablePartialEq::new(coord);
1271 let id = match vtx_to_id.entry(key) {
1272 Entry::Occupied(entry) => entry.into_mut(),
1273 Entry::Vacant(entry) => entry.insert(new_vertices.len() as u32),
1274 };
1275
1276 if *id == new_vertices.len() as u32 {
1277 new_vertices.push(coord);
1278 }
1279
1280 *id
1281 }
1282
1283 for t in self.indices.iter() {
1284 let va = resolve_coord_id(
1285 self.vertices[t[0] as usize],
1286 &mut vtx_to_id,
1287 &mut new_vertices,
1288 );
1289
1290 let vb = resolve_coord_id(
1291 self.vertices[t[1] as usize],
1292 &mut vtx_to_id,
1293 &mut new_vertices,
1294 );
1295
1296 let vc = resolve_coord_id(
1297 self.vertices[t[2] as usize],
1298 &mut vtx_to_id,
1299 &mut new_vertices,
1300 );
1301
1302 let is_degenerate = va == vb || va == vc || vb == vc;
1303
1304 if !is_degenerate || !delete_degenerate_triangles {
1305 if delete_duplicate_triangles {
1306 let (c, b, a) = crate::utils::sort3(&va, &vb, &vc);
1307 if triangle_set.insert((*a, *b, *c)) {
1308 new_indices.push([va, vb, vc])
1309 }
1310 } else {
1311 new_indices.push([va, vb, vc]);
1312 }
1313 }
1314 }
1315
1316 new_vertices.shrink_to_fit();
1317
1318 self.vertices = new_vertices;
1319 self.indices = new_indices;
1320
1321 // Vertices and indices changed: the pseudo-normals are no longer valid.
1322 #[cfg(feature = "dim3")]
1323 if self.pseudo_normals.is_some() {
1324 self.compute_pseudo_normals();
1325 }
1326
1327 // Vertices and indices changed: the topology no longer valid.
1328 #[cfg(feature = "dim3")]
1329 if self.topology.is_some() {
1330 let _ = self.compute_topology(false);
1331 }
1332 }
1333
1334 #[cfg(feature = "dim3")]
1335 /// Computes the pseudo-normals used for solid point-projection.
1336 ///
1337 /// This computes the pseudo-normals needed by the point containment test described in
1338 /// "Signed distance computation using the angle weighted pseudonormal", Baerentzen, et al.
1339 /// DOI: 10.1109/TVCG.2005.49
1340 ///
1341 /// For the point-containment test to properly detect the inside of the trimesh (i.e. to return
1342 /// `proj.is_inside = true`), the trimesh must:
1343 /// - Be manifold (closed, no t-junctions, etc.)
1344 /// - Be oriented with outward normals.
1345 ///
1346 /// If the trimesh is correctly oriented, but is manifold everywhere except at its boundaries,
1347 /// then the computed pseudo-normals will provide correct point-containment test results except
1348 /// for points closest to the boundary of the mesh.
1349 ///
1350 /// It may be useful to call `self.remove_duplicate_vertices()` before this method, in order to fix the
1351 /// index buffer if some of the vertices of this trimesh are duplicated.
1352 fn compute_pseudo_normals(&mut self) {
1353 let mut vertices_pseudo_normal = vec![Vector::ZERO; self.vertices().len()];
1354 let mut edges_pseudo_normal = HashMap::default();
1355 let mut edges_multiplicity = HashMap::default();
1356
1357 for idx in self.indices() {
1358 let vtx = self.vertices();
1359 let tri = Triangle::new(
1360 vtx[idx[0] as usize],
1361 vtx[idx[1] as usize],
1362 vtx[idx[2] as usize],
1363 );
1364
1365 if let Some(n) = tri.normal() {
1366 let ang1 = (tri.b - tri.a).angle(tri.c - tri.a);
1367 let ang2 = (tri.a - tri.b).angle(tri.c - tri.b);
1368 let ang3 = (tri.b - tri.c).angle(tri.a - tri.c);
1369
1370 vertices_pseudo_normal[idx[0] as usize] += n * ang1;
1371 vertices_pseudo_normal[idx[1] as usize] += n * ang2;
1372 vertices_pseudo_normal[idx[2] as usize] += n * ang3;
1373
1374 let edges = [
1375 SortedPair::new(idx[0], idx[1]),
1376 SortedPair::new(idx[0], idx[2]),
1377 SortedPair::new(idx[1], idx[2]),
1378 ];
1379
1380 for edge in &edges {
1381 let edge_n = edges_pseudo_normal.entry(*edge).or_insert(Vector::ZERO);
1382 *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.
1383 let edge_mult = edges_multiplicity.entry(*edge).or_insert(0);
1384 *edge_mult += 1;
1385 }
1386 }
1387 }
1388
1389 let edges_pseudo_normal = self
1390 .indices()
1391 .iter()
1392 .map(|idx| {
1393 let e0 = SortedPair::new(idx[0], idx[1]);
1394 let e1 = SortedPair::new(idx[1], idx[2]);
1395 let e2 = SortedPair::new(idx[2], idx[0]);
1396 let default = Vector::ZERO;
1397 [
1398 edges_pseudo_normal.get(&e0).copied().unwrap_or(default),
1399 edges_pseudo_normal.get(&e1).copied().unwrap_or(default),
1400 edges_pseudo_normal.get(&e2).copied().unwrap_or(default),
1401 ]
1402 })
1403 .collect();
1404
1405 self.pseudo_normals = Some(TriMeshPseudoNormals {
1406 vertices_pseudo_normal,
1407 edges_pseudo_normal,
1408 })
1409 }
1410
1411 fn delete_bad_topology_triangles(&mut self) {
1412 let mut half_edge_set = HashSet::default();
1413 let mut deleted_any = false;
1414
1415 // First, create three half-edges for each face.
1416 self.indices.retain(|idx| {
1417 if idx[0] == idx[1] || idx[0] == idx[2] || idx[1] == idx[2] {
1418 deleted_any = true;
1419 return false;
1420 }
1421
1422 for k in 0..3 {
1423 let edge_key = (idx[k as usize], idx[(k as usize + 1) % 3]);
1424 if half_edge_set.contains(&edge_key) {
1425 deleted_any = true;
1426 return false;
1427 }
1428 }
1429
1430 for k in 0..3 {
1431 let edge_key = (idx[k as usize], idx[(k as usize + 1) % 3]);
1432 let _ = half_edge_set.insert(edge_key);
1433 }
1434
1435 true
1436 });
1437 }
1438
1439 /// Computes half-edge topological information for this triangle mesh, based on its index buffer only.
1440 ///
1441 /// This computes the half-edge representation of this triangle mesh’s topology. This is useful for advanced
1442 /// geometric operations like trimesh-trimesh intersection geometry computation.
1443 ///
1444 /// It may be useful to call `self.merge_duplicate_vertices(true, true)` before this method, in order to fix the
1445 /// index buffer if some of the vertices of this trimesh are duplicated.
1446 ///
1447 /// # Return
1448 /// Returns `true` if the computation succeeded. Returns `false` if this mesh can’t have an half-edge representation
1449 /// because at least three faces share the same edge.
1450 fn compute_topology(&mut self, delete_bad_triangles: bool) -> Result<(), TopologyError> {
1451 if delete_bad_triangles {
1452 self.delete_bad_topology_triangles();
1453 }
1454
1455 let mut topology = TriMeshTopology::default();
1456 let mut half_edge_map = HashMap::default();
1457
1458 topology.vertices.resize(
1459 self.vertices.len(),
1460 TopoVertex {
1461 half_edge: u32::MAX,
1462 },
1463 );
1464
1465 // First, create three half-edges for each face.
1466 for (fid, idx) in self.indices.iter().enumerate() {
1467 let half_edge_base_id = topology.half_edges.len() as u32;
1468
1469 if idx[0] == idx[1] || idx[0] == idx[2] || idx[1] == idx[2] {
1470 return Err(TopologyError::BadTriangle(fid as u32));
1471 }
1472
1473 for k in 0u32..3 {
1474 let half_edge = TopoHalfEdge {
1475 next: half_edge_base_id + (k + 1) % 3,
1476 // We don’t know which one it is yet.
1477 // If the twin doesn’t exist, we use `u32::MAX` as
1478 // it’s (invalid) index. This value can be relied on
1479 // by other algorithms.
1480 twin: u32::MAX,
1481 vertex: idx[k as usize],
1482 face: fid as u32,
1483 };
1484 topology.half_edges.push(half_edge);
1485
1486 let edge_key = (idx[k as usize], idx[(k as usize + 1) % 3]);
1487
1488 if let Some(existing) = half_edge_map.insert(edge_key, half_edge_base_id + k) {
1489 // If the same edge already exists (with the same vertex order) then
1490 // we have two triangles sharing the same but with opposite incompatible orientations.
1491 return Err(TopologyError::BadAdjacentTrianglesOrientation {
1492 edge: edge_key,
1493 triangle1: topology.half_edges[existing as usize].face,
1494 triangle2: fid as u32,
1495 });
1496 }
1497
1498 topology.vertices[idx[k as usize] as usize].half_edge = half_edge_base_id + k;
1499 }
1500
1501 topology.faces.push(TopoFace {
1502 half_edge: half_edge_base_id,
1503 })
1504 }
1505
1506 // Second, identify twins.
1507 for (key, he1) in &half_edge_map {
1508 if key.0 < key.1 {
1509 // Test, to avoid checking the same pair twice.
1510 if let Some(he2) = half_edge_map.get(&(key.1, key.0)) {
1511 topology.half_edges[*he1 as usize].twin = *he2;
1512 topology.half_edges[*he2 as usize].twin = *he1;
1513 }
1514 }
1515 }
1516
1517 self.topology = Some(topology);
1518
1519 Ok(())
1520 }
1521
1522 // NOTE: this is private because that calculation is controlled by TriMeshFlags::CONNECTED_COMPONENTS
1523 // TODO: we should remove the CONNECTED_COMPONENTS flags and just have this be a free function.
1524 // TODO: this should be no_std compatible once ena is or once we have an alternative for it.
1525 #[cfg(feature = "std")]
1526 fn compute_connected_components(&mut self) {
1527 use ena::unify::{InPlaceUnificationTable, UnifyKey};
1528
1529 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
1530 struct IntKey(u32);
1531
1532 impl UnifyKey for IntKey {
1533 type Value = ();
1534 fn index(&self) -> u32 {
1535 self.0
1536 }
1537 fn from_index(u: u32) -> IntKey {
1538 IntKey(u)
1539 }
1540 fn tag() -> &'static str {
1541 "IntKey"
1542 }
1543 }
1544
1545 let mut ufind: InPlaceUnificationTable<IntKey> = InPlaceUnificationTable::new();
1546 let mut face_colors = vec![u32::MAX; self.indices.len()];
1547 let mut ranges = vec![0];
1548 let mut vertex_to_range = vec![u32::MAX; self.vertices.len()];
1549 let mut grouped_faces = vec![u32::MAX; self.indices.len()];
1550 let mut vertex_to_key = vec![IntKey(u32::MAX); self.vertices.len()];
1551
1552 let mut vertex_key = |id: u32, ufind: &mut InPlaceUnificationTable<IntKey>| {
1553 if vertex_to_key[id as usize].0 == u32::MAX {
1554 let new_key = ufind.new_key(());
1555 vertex_to_key[id as usize] = new_key;
1556 new_key
1557 } else {
1558 vertex_to_key[id as usize]
1559 }
1560 };
1561
1562 for idx in self.indices() {
1563 let keys = idx.map(|i| vertex_key(i, &mut ufind));
1564 ufind.union(keys[0], keys[1]);
1565 ufind.union(keys[1], keys[2]);
1566 ufind.union(keys[2], keys[0]);
1567 }
1568
1569 for (idx, face_color) in self.indices().iter().zip(face_colors.iter_mut()) {
1570 debug_assert_eq!(
1571 ufind.find(vertex_to_key[idx[0] as usize]),
1572 ufind.find(vertex_to_key[idx[1] as usize])
1573 );
1574 debug_assert_eq!(
1575 ufind.find(vertex_to_key[idx[0] as usize]),
1576 ufind.find(vertex_to_key[idx[2] as usize])
1577 );
1578
1579 let group_index = ufind.find(vertex_to_key[idx[0] as usize]).0 as usize;
1580
1581 if vertex_to_range[group_index] == u32::MAX {
1582 // Additional range
1583 ranges.push(0);
1584 vertex_to_range[group_index] = ranges.len() as u32 - 1;
1585 }
1586
1587 let range_id = vertex_to_range[group_index];
1588 ranges[range_id as usize] += 1;
1589 // NOTE: the range_id points to the range upper bound. The face color is the range lower bound.
1590 *face_color = range_id - 1;
1591 }
1592
1593 // Cumulated sum on range indices, to find the first index faces need to be inserted into
1594 // for each range.
1595 for i in 1..ranges.len() {
1596 ranges[i] += ranges[i - 1];
1597 }
1598
1599 debug_assert_eq!(*ranges.last().unwrap(), self.indices().len());
1600
1601 // Group faces.
1602 let mut insertion_in_range_index = ranges.clone();
1603 for (face_id, face_color) in face_colors.iter().enumerate() {
1604 let insertion_index = &mut insertion_in_range_index[*face_color as usize];
1605 grouped_faces[*insertion_index] = face_id as u32;
1606 *insertion_index += 1;
1607 }
1608
1609 self.connected_components = Some(TriMeshConnectedComponents {
1610 face_colors,
1611 grouped_faces,
1612 ranges,
1613 })
1614 }
1615
1616 #[allow(dead_code)] // Useful for testing.
1617 pub(crate) fn assert_half_edge_topology_is_valid(&self) {
1618 let topo = self
1619 .topology
1620 .as_ref()
1621 .expect("No topology information found.");
1622 assert_eq!(self.vertices.len(), topo.vertices.len());
1623 assert_eq!(self.indices.len(), topo.faces.len());
1624
1625 for (face_id, (face, idx)) in topo.faces.iter().zip(self.indices.iter()).enumerate() {
1626 let he0 = topo.half_edges[face.half_edge as usize];
1627 assert_eq!(he0.face, face_id as u32);
1628 assert_eq!(he0.vertex, idx[0]);
1629 let he1 = topo.half_edges[he0.next as usize];
1630 assert_eq!(he1.face, face_id as u32);
1631 assert_eq!(he1.vertex, idx[1]);
1632 let he2 = topo.half_edges[he1.next as usize];
1633 assert_eq!(he2.face, face_id as u32);
1634 assert_eq!(he2.vertex, idx[2]);
1635 assert_eq!(he2.next, face.half_edge);
1636 }
1637
1638 for he in &topo.half_edges {
1639 let idx = &self.indices[he.face as usize];
1640 assert!(he.vertex == idx[0] || he.vertex == idx[1] || he.vertex == idx[2]);
1641 }
1642 }
1643
1644 /// An iterator through all the triangles of this mesh.
1645 ///
1646 /// Returns an iterator that yields [`Triangle`] shapes representing each
1647 /// triangle in the mesh. Each triangle contains the actual 3D coordinates
1648 /// of its three vertices (not indices).
1649 ///
1650 /// # Performance
1651 ///
1652 /// The iterator performs vertex lookups on-the-fly, so iterating through
1653 /// all triangles is O(n) where n is the number of triangles.
1654 ///
1655 /// # Example
1656 ///
1657 /// ```
1658 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1659 /// use parry3d::shape::TriMesh;
1660 /// use parry3d::math::Vector;
1661 ///
1662 /// let vertices = vec![
1663 /// Vector::ZERO,
1664 /// Vector::new(1.0, 0.0, 0.0),
1665 /// Vector::new(0.0, 1.0, 0.0),
1666 /// Vector::new(1.0, 1.0, 0.0),
1667 /// ];
1668 /// let indices = vec![[0, 1, 2], [1, 3, 2]];
1669 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1670 ///
1671 /// // Iterate through all triangles
1672 /// for triangle in mesh.triangles() {
1673 /// println!("Triangle: {:?}, {:?}, {:?}", triangle.a, triangle.b, triangle.c);
1674 /// }
1675 ///
1676 /// // Count triangles with specific properties
1677 /// let count = mesh.triangles()
1678 /// .filter(|tri| tri.area() > 0.1)
1679 /// .count();
1680 /// # }
1681 /// ```
1682 pub fn triangles(&self) -> impl ExactSizeIterator<Item = Triangle> + '_ {
1683 self.indices.iter().map(move |ids| {
1684 Triangle::new(
1685 self.vertices[ids[0] as usize],
1686 self.vertices[ids[1] as usize],
1687 self.vertices[ids[2] as usize],
1688 )
1689 })
1690 }
1691
1692 #[cfg(feature = "dim3")]
1693 /// Gets the normal of the triangle represented by `feature`.
1694 pub fn feature_normal(&self, feature: FeatureId) -> Option<Vector> {
1695 match feature {
1696 FeatureId::Face(i) => self
1697 .triangle(i % self.num_triangles() as u32)
1698 .feature_normal(FeatureId::Face(0)),
1699 _ => None,
1700 }
1701 }
1702}
1703
1704impl TriMesh {
1705 /// The flags of this triangle mesh.
1706 ///
1707 /// Returns the [`TriMeshFlags`] that were used to construct this mesh,
1708 /// indicating which optional features are enabled.
1709 ///
1710 /// # Example
1711 ///
1712 /// ```
1713 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1714 /// use parry3d::shape::{TriMesh, TriMeshFlags};
1715 /// use parry3d::math::Vector;
1716 ///
1717 /// let vertices = vec![
1718 /// Vector::ZERO,
1719 /// Vector::new(1.0, 0.0, 0.0),
1720 /// Vector::new(0.0, 1.0, 0.0),
1721 /// ];
1722 /// let indices = vec![[0, 1, 2]];
1723 ///
1724 /// let flags = TriMeshFlags::HALF_EDGE_TOPOLOGY;
1725 /// let mesh = TriMesh::with_flags(vertices, indices, flags).unwrap();
1726 ///
1727 /// assert!(mesh.flags().contains(TriMeshFlags::HALF_EDGE_TOPOLOGY));
1728 /// # }
1729 /// ```
1730 pub fn flags(&self) -> TriMeshFlags {
1731 self.flags
1732 }
1733
1734 /// Compute the axis-aligned bounding box of this triangle mesh.
1735 ///
1736 /// Returns the AABB that tightly bounds all triangles of the mesh after
1737 /// applying the given isometry transformation.
1738 ///
1739 /// # Arguments
1740 ///
1741 /// * `pos` - The position/orientation of the mesh in world space
1742 ///
1743 /// # Example
1744 ///
1745 /// ```
1746 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1747 /// use parry3d::shape::TriMesh;
1748 /// use parry3d::math::{Vector, Pose};
1749 ///
1750 /// let vertices = vec![
1751 /// Vector::ZERO,
1752 /// Vector::new(1.0, 0.0, 0.0),
1753 /// Vector::new(0.0, 1.0, 0.0),
1754 /// ];
1755 /// let indices = vec![[0, 1, 2]];
1756 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1757 ///
1758 /// let identity = Pose::identity();
1759 /// let aabb = mesh.aabb(&identity);
1760 ///
1761 /// // The AABB contains all vertices
1762 /// assert!(aabb.contains_local_point(Vector::new(0.5, 0.5, 0.0)));
1763 /// # }
1764 /// ```
1765 pub fn aabb(&self, pos: &Pose) -> Aabb {
1766 self.bvh.root_aabb().transform_by(pos)
1767 }
1768
1769 /// Gets the local axis-aligned bounding box of this triangle mesh.
1770 ///
1771 /// Returns the AABB in the mesh's local coordinate system (without any
1772 /// transformation applied). This is faster than [`TriMesh::aabb`] when
1773 /// no transformation is needed.
1774 ///
1775 /// # Example
1776 ///
1777 /// ```
1778 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1779 /// use parry3d::shape::TriMesh;
1780 /// use parry3d::math::Vector;
1781 ///
1782 /// let vertices = vec![
1783 /// Vector::new(-1.0, -1.0, 0.0),
1784 /// Vector::new(1.0, -1.0, 0.0),
1785 /// Vector::new(0.0, 1.0, 0.0),
1786 /// ];
1787 /// let indices = vec![[0, 1, 2]];
1788 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1789 ///
1790 /// let aabb = mesh.local_aabb();
1791 /// assert_eq!(aabb.mins.x, -1.0);
1792 /// assert_eq!(aabb.maxs.x, 1.0);
1793 /// # }
1794 /// ```
1795 pub fn local_aabb(&self) -> Aabb {
1796 self.bvh.root_aabb()
1797 }
1798
1799 /// The acceleration structure used by this triangle-mesh.
1800 ///
1801 /// Returns a reference to the BVH (Bounding Volume Hierarchy) that
1802 /// accelerates spatial queries on this mesh. The BVH is used internally
1803 /// for ray casting, collision detection, and other geometric queries.
1804 ///
1805 /// # Example
1806 ///
1807 /// ```
1808 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1809 /// use parry3d::shape::TriMesh;
1810 /// use parry3d::math::Vector;
1811 ///
1812 /// let vertices = vec![
1813 /// Vector::ZERO,
1814 /// Vector::new(1.0, 0.0, 0.0),
1815 /// Vector::new(0.0, 1.0, 0.0),
1816 /// ];
1817 /// let indices = vec![[0, 1, 2]];
1818 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1819 ///
1820 /// let bvh = mesh.bvh();
1821 /// // The BVH can be used for advanced spatial queries
1822 /// assert!(!bvh.is_empty());
1823 /// # }
1824 /// ```
1825 pub fn bvh(&self) -> &Bvh {
1826 &self.bvh
1827 }
1828
1829 /// The number of triangles forming this mesh.
1830 ///
1831 /// # Example
1832 ///
1833 /// ```
1834 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1835 /// use parry3d::shape::TriMesh;
1836 /// use parry3d::math::Vector;
1837 ///
1838 /// let vertices = vec![
1839 /// Vector::ZERO,
1840 /// Vector::new(1.0, 0.0, 0.0),
1841 /// Vector::new(0.0, 1.0, 0.0),
1842 /// Vector::new(1.0, 1.0, 0.0),
1843 /// ];
1844 /// let indices = vec![[0, 1, 2], [1, 3, 2]];
1845 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1846 ///
1847 /// assert_eq!(mesh.num_triangles(), 2);
1848 /// # }
1849 /// ```
1850 pub fn num_triangles(&self) -> usize {
1851 self.indices.len()
1852 }
1853
1854 /// Does the given feature ID identify a backface of this trimesh?
1855 pub fn is_backface(&self, feature: FeatureId) -> bool {
1856 if let FeatureId::Face(i) = feature {
1857 i >= self.indices.len() as u32
1858 } else {
1859 false
1860 }
1861 }
1862
1863 /// Get the `i`-th triangle of this mesh.
1864 ///
1865 /// Returns a [`Triangle`] shape with the actual vertex coordinates (not indices)
1866 /// of the requested triangle. The triangle index must be less than the number
1867 /// of triangles in the mesh.
1868 ///
1869 /// # Arguments
1870 ///
1871 /// * `i` - The index of the triangle to retrieve (0-based)
1872 ///
1873 /// # Panics
1874 ///
1875 /// Panics if `i >= self.num_triangles()`.
1876 ///
1877 /// # Example
1878 ///
1879 /// ```
1880 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1881 /// use parry3d::shape::TriMesh;
1882 /// use parry3d::math::Vector;
1883 ///
1884 /// let vertices = vec![
1885 /// Vector::ZERO,
1886 /// Vector::new(1.0, 0.0, 0.0),
1887 /// Vector::new(0.0, 1.0, 0.0),
1888 /// ];
1889 /// let indices = vec![[0, 1, 2]];
1890 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1891 ///
1892 /// let triangle = mesh.triangle(0);
1893 /// assert_eq!(triangle.a, Vector::ZERO);
1894 /// assert_eq!(triangle.b, Vector::new(1.0, 0.0, 0.0));
1895 /// assert_eq!(triangle.c, Vector::new(0.0, 1.0, 0.0));
1896 /// # }
1897 /// ```
1898 pub fn triangle(&self, i: u32) -> Triangle {
1899 let idx = self.indices[i as usize];
1900 Triangle::new(
1901 self.vertices[idx[0] as usize],
1902 self.vertices[idx[1] as usize],
1903 self.vertices[idx[2] as usize],
1904 )
1905 }
1906
1907 /// Returns the pseudo-normals of one of this mesh’s triangles, if it was computed.
1908 ///
1909 /// This returns `None` if the pseudo-normals of this triangle were not computed.
1910 /// To have its pseudo-normals computed, be sure to set the [`TriMeshFlags`] so that
1911 /// they contain the [`TriMeshFlags::FIX_INTERNAL_EDGES`] flag.
1912 #[cfg(feature = "dim3")]
1913 pub fn triangle_normal_constraints(&self, i: u32) -> Option<TrianglePseudoNormals> {
1914 if self.flags.contains(TriMeshFlags::FIX_INTERNAL_EDGES) {
1915 let triangle = self.triangle(i);
1916 let pseudo_normals = self.pseudo_normals.as_ref()?;
1917 let edges_pseudo_normals = pseudo_normals.edges_pseudo_normal[i as usize];
1918
1919 // TODO: could the pseudo-normal be pre-normalized instead of having to renormalize
1920 // every time we need them?
1921 Some(TrianglePseudoNormals {
1922 face: triangle.normal()?,
1923 edges: [
1924 (edges_pseudo_normals[0]).try_normalize()?,
1925 (edges_pseudo_normals[1]).try_normalize()?,
1926 (edges_pseudo_normals[2]).try_normalize()?,
1927 ],
1928 })
1929 } else {
1930 None
1931 }
1932 }
1933
1934 #[cfg(feature = "dim2")]
1935 #[doc(hidden)]
1936 pub fn triangle_normal_constraints(&self, _i: u32) -> Option<TrianglePseudoNormals> {
1937 None
1938 }
1939
1940 /// The vertex buffer of this mesh.
1941 ///
1942 /// Returns a slice containing all vertex positions as 3D points.
1943 /// The vertices are stored in the order they were provided during construction.
1944 ///
1945 /// # Example
1946 ///
1947 /// ```
1948 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1949 /// use parry3d::shape::TriMesh;
1950 /// use parry3d::math::Vector;
1951 ///
1952 /// let vertices = vec![
1953 /// Vector::ZERO,
1954 /// Vector::new(1.0, 0.0, 0.0),
1955 /// Vector::new(0.0, 1.0, 0.0),
1956 /// ];
1957 /// let indices = vec![[0, 1, 2]];
1958 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1959 ///
1960 /// assert_eq!(mesh.vertices().len(), 3);
1961 /// assert_eq!(mesh.vertices()[0], Vector::ZERO);
1962 /// # }
1963 /// ```
1964 pub fn vertices(&self) -> &[Vector] {
1965 &self.vertices
1966 }
1967
1968 /// The index buffer of this mesh.
1969 ///
1970 /// Returns a slice of triangles, where each triangle is represented as an
1971 /// array of three vertex indices. The indices reference positions in the
1972 /// vertex buffer returned by [`TriMesh::vertices`].
1973 ///
1974 /// # Example
1975 ///
1976 /// ```
1977 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1978 /// use parry3d::shape::TriMesh;
1979 /// use parry3d::math::Vector;
1980 ///
1981 /// let vertices = vec![
1982 /// Vector::ZERO,
1983 /// Vector::new(1.0, 0.0, 0.0),
1984 /// Vector::new(0.0, 1.0, 0.0),
1985 /// Vector::new(1.0, 1.0, 0.0),
1986 /// ];
1987 /// let indices = vec![[0, 1, 2], [1, 3, 2]];
1988 /// let mesh = TriMesh::new(vertices, indices).unwrap();
1989 ///
1990 /// assert_eq!(mesh.indices().len(), 2);
1991 /// assert_eq!(mesh.indices()[0], [0, 1, 2]);
1992 /// assert_eq!(mesh.indices()[1], [1, 3, 2]);
1993 /// # }
1994 /// ```
1995 pub fn indices(&self) -> &[[u32; 3]] {
1996 &self.indices
1997 }
1998
1999 /// Returns the topology information of this trimesh, if it has been computed.
2000 ///
2001 /// Topology information includes half-edge data structures that describe
2002 /// adjacency relationships between vertices, edges, and faces. This is
2003 /// computed when the [`TriMeshFlags::HALF_EDGE_TOPOLOGY`] flag is set.
2004 ///
2005 /// # Example
2006 ///
2007 /// ```
2008 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
2009 /// use parry3d::shape::{TriMesh, TriMeshFlags};
2010 /// use parry3d::math::Vector;
2011 ///
2012 /// let vertices = vec![
2013 /// Vector::ZERO,
2014 /// Vector::new(1.0, 0.0, 0.0),
2015 /// Vector::new(0.0, 1.0, 0.0),
2016 /// ];
2017 /// let indices = vec![[0, 1, 2]];
2018 ///
2019 /// // Without topology flag
2020 /// let mesh = TriMesh::new(vertices.clone(), indices.clone()).unwrap();
2021 /// assert!(mesh.topology().is_none());
2022 ///
2023 /// // With topology flag
2024 /// let mesh = TriMesh::with_flags(
2025 /// vertices,
2026 /// indices,
2027 /// TriMeshFlags::HALF_EDGE_TOPOLOGY
2028 /// ).unwrap();
2029 /// assert!(mesh.topology().is_some());
2030 /// # }
2031 /// ```
2032 pub fn topology(&self) -> Option<&TriMeshTopology> {
2033 self.topology.as_ref()
2034 }
2035
2036 /// Returns the connected-component information of this trimesh, if it has been computed.
2037 ///
2038 /// Connected components represent separate, non-connected parts of the mesh.
2039 /// This is computed when the [`TriMeshFlags::CONNECTED_COMPONENTS`] flag is set
2040 /// (requires the `std` feature).
2041 ///
2042 /// # Example
2043 ///
2044 /// ```
2045 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
2046 /// # #[cfg(feature = "std")] {
2047 /// use parry3d::shape::{TriMesh, TriMeshFlags};
2048 /// use parry3d::math::Vector;
2049 ///
2050 /// // Create two separate triangles (not connected)
2051 /// let vertices = vec![
2052 /// // First triangle
2053 /// Vector::ZERO,
2054 /// Vector::new(1.0, 0.0, 0.0),
2055 /// Vector::new(0.0, 1.0, 0.0),
2056 /// // Second triangle (separate)
2057 /// Vector::new(5.0, 0.0, 0.0),
2058 /// Vector::new(6.0, 0.0, 0.0),
2059 /// Vector::new(5.0, 1.0, 0.0),
2060 /// ];
2061 /// let indices = vec![[0, 1, 2], [3, 4, 5]];
2062 ///
2063 /// let mesh = TriMesh::with_flags(
2064 /// vertices,
2065 /// indices,
2066 /// TriMeshFlags::CONNECTED_COMPONENTS
2067 /// ).unwrap();
2068 ///
2069 /// if let Some(cc) = mesh.connected_components() {
2070 /// assert_eq!(cc.num_connected_components(), 2);
2071 /// }
2072 /// # }
2073 /// # }
2074 /// ```
2075 pub fn connected_components(&self) -> Option<&TriMeshConnectedComponents> {
2076 self.connected_components.as_ref()
2077 }
2078
2079 /// Returns the connected-component of this mesh.
2080 ///
2081 /// The connected-components are returned as a set of `TriMesh` build with the given `flags`.
2082 pub fn connected_component_meshes(
2083 &self,
2084 flags: TriMeshFlags,
2085 ) -> Option<Vec<Result<TriMesh, TriMeshBuilderError>>> {
2086 self.connected_components()
2087 .map(|cc| cc.to_meshes(self, flags))
2088 }
2089
2090 /// The pseudo-normals of this triangle mesh, if they have been computed.
2091 #[cfg(feature = "dim3")]
2092 pub fn pseudo_normals(&self) -> Option<&TriMeshPseudoNormals> {
2093 self.pseudo_normals.as_ref()
2094 }
2095
2096 /// The pseudo-normals of this triangle mesh, if they have been computed **and** this mesh was
2097 /// marked as [`TriMeshFlags::ORIENTED`].
2098 #[cfg(feature = "dim3")]
2099 pub fn pseudo_normals_if_oriented(&self) -> Option<&TriMeshPseudoNormals> {
2100 if self.flags.intersects(TriMeshFlags::ORIENTED) {
2101 self.pseudo_normals.as_ref()
2102 } else {
2103 None
2104 }
2105 }
2106}
2107
2108#[cfg(feature = "dim3")]
2109impl From<crate::shape::HeightField> for TriMesh {
2110 fn from(heightfield: crate::shape::HeightField) -> Self {
2111 let (vtx, idx) = heightfield.to_trimesh();
2112 TriMesh::new(vtx, idx).unwrap()
2113 }
2114}
2115
2116#[cfg(feature = "dim3")]
2117impl From<Cuboid> for TriMesh {
2118 fn from(cuboid: Cuboid) -> Self {
2119 let (vtx, idx) = cuboid.to_trimesh();
2120 TriMesh::new(vtx, idx).unwrap()
2121 }
2122}
2123
2124impl CompositeShape for TriMesh {
2125 fn map_part_at(
2126 &self,
2127 i: u32,
2128 f: &mut dyn FnMut(Option<&Pose>, &dyn Shape, Option<&dyn NormalConstraints>),
2129 ) {
2130 let tri = self.triangle(i);
2131 let normals = self.triangle_normal_constraints(i);
2132 f(
2133 None,
2134 &tri,
2135 normals.as_ref().map(|n| n as &dyn NormalConstraints),
2136 )
2137 }
2138
2139 fn bvh(&self) -> &Bvh {
2140 &self.bvh
2141 }
2142}
2143
2144impl TypedCompositeShape for TriMesh {
2145 type PartShape = Triangle;
2146 type PartNormalConstraints = TrianglePseudoNormals;
2147
2148 #[inline(always)]
2149 fn map_typed_part_at<T>(
2150 &self,
2151 i: u32,
2152 mut f: impl FnMut(Option<&Pose>, &Self::PartShape, Option<&Self::PartNormalConstraints>) -> T,
2153 ) -> Option<T> {
2154 let tri = self.triangle(i);
2155 let pseudo_normals = self.triangle_normal_constraints(i);
2156 Some(f(None, &tri, pseudo_normals.as_ref()))
2157 }
2158
2159 #[inline(always)]
2160 fn map_untyped_part_at<T>(
2161 &self,
2162 i: u32,
2163 mut f: impl FnMut(Option<&Pose>, &dyn Shape, Option<&dyn NormalConstraints>) -> T,
2164 ) -> Option<T> {
2165 let tri = self.triangle(i);
2166 let pseudo_normals = self.triangle_normal_constraints(i);
2167 Some(f(
2168 None,
2169 &tri,
2170 pseudo_normals.as_ref().map(|n| n as &dyn NormalConstraints),
2171 ))
2172 }
2173}
2174
2175#[cfg(test)]
2176mod test {
2177 use crate::math::{Real, Vector};
2178 use crate::shape::{Cuboid, TriMesh, TriMeshFlags};
2179
2180 #[test]
2181 fn trimesh_error_empty_indices() {
2182 assert!(
2183 TriMesh::with_flags(vec![], vec![], TriMeshFlags::empty()).is_err(),
2184 "A triangle mesh with no triangles is invalid."
2185 );
2186 }
2187
2188 #[test]
2189 fn connected_components() {
2190 let (vtx, idx) = Cuboid::new(Vector::splat(0.5)).to_trimesh();
2191
2192 // Push 10 copy of the mesh, each time pushed with an offset.
2193 let mut mesh = TriMesh::new(vtx.clone(), idx.clone()).unwrap();
2194
2195 for i in 1..10 {
2196 let cc_vtx = vtx
2197 .iter()
2198 .map(|pt| *pt + Vector::splat(2.0 * i as Real))
2199 .collect();
2200
2201 let to_append = TriMesh::new(cc_vtx, idx.clone()).unwrap();
2202 mesh.append(&to_append);
2203 }
2204
2205 mesh.set_flags(TriMeshFlags::CONNECTED_COMPONENTS).unwrap();
2206 let connected_components = mesh.connected_components().unwrap();
2207 assert_eq!(connected_components.num_connected_components(), 10);
2208
2209 let cc_meshes = connected_components.to_meshes(&mesh, TriMeshFlags::empty());
2210
2211 for cc in cc_meshes {
2212 let cc = cc.unwrap();
2213 assert_eq!(cc.vertices.len(), vtx.len());
2214 assert_eq!(cc.indices.len(), idx.len());
2215 }
2216 }
2217}