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