Skip to main content

rapier3d/dynamics/soft_body/collision_mesh/
collision_mesh_accessors.rs

1//! Read accessors of a collision mesh: identity, elements and edges, vertices and the particles
2//! a contact at a vertex acts through.
3
4use crate::geometry::ColliderHandle;
5use crate::math::{DIM, Real, Vector};
6
7use super::{SoftBody, SoftCollisionMesh, SoftMeshCellBinding, SoftMeshId, SoftMeshMapping};
8
9impl SoftCollisionMesh {
10    /// Where this mesh lives in its soft body.
11    pub fn id(&self) -> SoftMeshId {
12        self.id
13    }
14
15    /// Records where this mesh was put (called when it joins a cluster).
16    pub(crate) fn set_id(&mut self, id: SoftMeshId) {
17        self.id = id;
18    }
19
20    /// The collider holding this mesh's shape (invalid for a mesh that is only drawn).
21    pub fn collider(&self) -> ColliderHandle {
22        self.collider
23    }
24
25    /// Whether this mesh collides (`false`: it is only drawn).
26    pub fn collision_enabled(&self) -> bool {
27        self.collision_enabled
28    }
29
30    /// The mesh's elements: segments in 2D, triangles in 3D, or segments in 3D for a wire
31    /// (`u32::MAX` fills the unused slot then; see [`Self::element`]).
32    pub fn indices(&self) -> &[[u32; DIM]] {
33        &self.indices
34    }
35
36    /// The vertices of the `i`-th element: two for a segment, `DIM` for a facet.
37    #[inline]
38    pub fn element(&self, i: usize) -> &[u32] {
39        super::soft_body_builder::element_vertices(&self.indices[i])
40    }
41
42    /// How many vertices this mesh's elements have: `2` for a wire (a rope in 3D, and every
43    /// mesh in 2D), `DIM` for a surface.
44    #[inline]
45    pub fn arity(&self) -> usize {
46        self.indices
47            .first()
48            .map_or(DIM, |e| super::soft_body_builder::element_vertices(e).len())
49    }
50
51    /// Whether this mesh is a wire (segments in 3D): a curve, with no inside and no facets.
52    #[inline]
53    pub fn is_wire(&self) -> bool {
54        self.arity() < DIM
55    }
56
57    /// The edges the `i`-th element owns: its three edges for a triangle, itself for a segment.
58    pub(crate) fn element_edge_ids(&self, i: usize) -> impl Iterator<Item = u32> + '_ {
59        #[cfg(feature = "dim3")]
60        let ids: &[u32] = if self.is_wire() {
61            &[]
62        } else {
63            super::soft_body_builder::element_vertices(&self.element_edges[i])
64        };
65        #[cfg(feature = "dim2")]
66        let ids: &[u32] = &[];
67        let own = ids.is_empty().then_some(i as u32);
68        own.into_iter().chain(ids.iter().copied())
69    }
70
71    /// The two vertices of an edge id (see [`Self::element_edge_ids`]).
72    pub(crate) fn edge_vertices(&self, edge: u32) -> [u32; 2] {
73        #[cfg(feature = "dim3")]
74        if !self.is_wire() {
75            return self.edges[edge as usize];
76        }
77        let element = self.element(edge as usize);
78        [element[0], element[1]]
79    }
80
81    /// The element owning an edge: a segment owns itself.
82    pub(crate) fn edge_owner(&self, edge: u32) -> u32 {
83        #[cfg(feature = "dim3")]
84        if !self.is_wire() {
85            return self.edge_owners[edge as usize];
86        }
87        edge
88    }
89
90    /// How this mesh's vertices follow the computational mesh.
91    pub fn binding(&self) -> &SoftMeshMapping {
92        &self.binding
93    }
94
95    /// Whether this mesh rides the body's cells rather than its particles.
96    pub fn is_skinned(&self) -> bool {
97        matches!(self.binding, SoftMeshMapping::Skinned { .. })
98    }
99
100    /// Whether this mesh is closed (every segment vertex / triangle edge shared by exactly two
101    /// elements).
102    pub fn is_closed(&self) -> bool {
103        self.closed
104    }
105
106    /// Whether this mesh's collider shape carries parry's `ORIENTED` flag, as of the last step.
107    pub fn is_oriented(&self) -> bool {
108        self.oriented
109    }
110
111    /// Whether this mesh encloses solid matter: closed and oriented. Its contacts with other
112    /// bodies are then oriented outward and nothing is held inside it; otherwise it is two-sided.
113    pub fn is_solid(&self) -> bool {
114        self.closed && self.oriented
115    }
116
117    /// Reads the orientation back from the collider's shape, which is the authority on it.
118    pub(crate) fn read_orientation(&mut self, colliders: &crate::geometry::ColliderSet) {
119        let Some(co) = colliders.get(self.collider) else {
120            return;
121        };
122        #[cfg(feature = "dim2")]
123        let oriented = co.shape().as_polyline().is_some_and(|polyline| {
124            polyline
125                .flags()
126                .contains(parry::shape::PolylineFlags::ORIENTED)
127        });
128        #[cfg(feature = "dim3")]
129        let oriented = co.shape().as_trimesh().is_some_and(|trimesh| {
130            trimesh
131                .flags()
132                .contains(parry::shape::TriMeshFlags::ORIENTED)
133        });
134        self.oriented = oriented;
135    }
136
137    /// Whether this mesh collides with itself.
138    pub fn self_contacts_enabled(&self) -> bool {
139        self.self_contacts
140    }
141
142    /// The number of vertices of this mesh.
143    pub fn vertex_count(&self) -> usize {
144        match &self.binding {
145            SoftMeshMapping::Direct { particles } => particles.len(),
146            SoftMeshMapping::Skinned { .. } => self.vertices.len(),
147        }
148    }
149
150    /// The cached world-space vertices of a skinned mesh (empty for a direct mesh, whose
151    /// vertices are its particles: see [`Self::vertex_positions`]).
152    pub fn vertices(&self) -> &[Vector] {
153        &self.vertices
154    }
155
156    /// The world-space position of every vertex of this mesh.
157    pub fn vertex_positions<'a>(
158        &'a self,
159        body: &'a SoftBody,
160    ) -> impl ExactSizeIterator<Item = Vector> + 'a {
161        (0..self.vertex_count()).map(move |i| self.vertex(body, i))
162    }
163
164    /// The world position of the `i`-th vertex.
165    pub fn vertex(&self, body: &SoftBody, i: usize) -> Vector {
166        match &self.binding {
167            SoftMeshMapping::Direct { particles } => body.particles[particles[i] as usize].position,
168            SoftMeshMapping::Skinned { .. } => self.vertices[i],
169        }
170    }
171
172    /// The `i`-th vertex as cached by [`Self::refresh_vertex_cache`] (the narrow-phase passes
173    /// read the cache, refreshed right before every narrow-phase update).
174    #[inline]
175    pub(crate) fn cached_vertex(&self, i: usize) -> Vector {
176        self.vertex_cache[i]
177    }
178
179    /// Refreshes the vertex cache from the particles (see [`Self::cached_vertex`]).
180    pub(crate) fn refresh_vertex_cache(&mut self, particles: &[super::super::SoftBodyParticle]) {
181        match &self.binding {
182            SoftMeshMapping::Direct { particles: map } => {
183                self.vertex_cache.clear();
184                self.vertex_cache
185                    .extend(map.iter().map(|&p| particles[p as usize].position));
186            }
187            SoftMeshMapping::Skinned { .. } => {
188                self.vertex_cache.clear();
189                self.vertex_cache.extend_from_slice(&self.vertices);
190            }
191        }
192    }
193
194    /// The world velocity of the `i`-th vertex: a skinned vertex moves with the cell holding it.
195    pub fn vertex_velocity(&self, body: &SoftBody, i: usize) -> Vector {
196        match &self.binding {
197            SoftMeshMapping::Direct { particles } => body.particles[particles[i] as usize].velocity,
198            SoftMeshMapping::Skinned { bindings } => {
199                let binding = &bindings[i];
200                let Some(cell) = body.cells.get(binding.cell as usize) else {
201                    return Vector::ZERO;
202                };
203                let mut velocity = Vector::ZERO;
204                for (vid, weight) in cell.vertices.iter().zip(&binding.weights) {
205                    velocity += body.particles[*vid as usize].velocity * *weight;
206                }
207                velocity
208            }
209        }
210    }
211
212    /// Whether the `i`-th vertex belongs to any element of this mesh.
213    pub(crate) fn vertex_on_surface(&self, i: usize) -> bool {
214        match (
215            self.vertex_elements_offsets.get(i),
216            self.vertex_elements_offsets.get(i + 1),
217        ) {
218            (Some(start), Some(end)) => end > start,
219            _ => false,
220        }
221    }
222
223    /// The particles a contact at the `i`-th vertex acts through, and their weights: the
224    /// vertex's own particle for a direct mesh, the particles of the cell holding it for a
225    /// skinned one.
226    pub fn vertex_anchors(&self, body: &SoftBody, i: usize) -> ([u32; DIM + 1], [Real; DIM + 1]) {
227        match &self.binding {
228            SoftMeshMapping::Direct { particles } => {
229                let mut ids = [u32::MAX; DIM + 1];
230                let mut weights = [0.0; DIM + 1];
231                ids[0] = particles[i];
232                weights[0] = 1.0;
233                (ids, weights)
234            }
235            SoftMeshMapping::Skinned { bindings } => {
236                match body.cells.get(bindings[i].cell as usize) {
237                    Some(cell) => (cell.vertices, bindings[i].weights),
238                    None => ([u32::MAX; DIM + 1], [0.0; DIM + 1]),
239                }
240            }
241        }
242    }
243
244    /// The particles a contact acts through and their weights (the element's own vertices for a
245    /// direct mesh, the bound cell's particles for a skinned one), plus the point the constraint
246    /// tracks; `weights` are the contact point's barycentric coordinates in `element`.
247    pub(crate) fn contact_anchors(
248        &self,
249        body: &SoftBody,
250        vertices: &[u32],
251        weights: &[Real],
252        point: Vector,
253    ) -> ([u32; DIM + 1], [Real; DIM + 1], Vector) {
254        let pad = |vertices: &[u32]| {
255            let mut ids = [u32::MAX; DIM + 1];
256            let mut ws = [0.0; DIM + 1];
257            let len = vertices.len().min(DIM + 1);
258            ids[..len].copy_from_slice(&vertices[..len]);
259            ws[..len].copy_from_slice(&weights[..len]);
260            (ids, ws, point)
261        };
262
263        match &self.binding {
264            SoftMeshMapping::Direct { particles } => {
265                let mut ids = [u32::MAX; DIM + 1];
266                let mut ws = [0.0; DIM + 1];
267                for k in 0..vertices.len().min(DIM + 1) {
268                    ids[k] = particles[vertices[k] as usize];
269                    ws[k] = weights[k];
270                }
271                (ids, ws, point)
272            }
273            SoftMeshMapping::Skinned { bindings } => self
274                .skinned_contact_anchors(body, bindings, vertices, weights, point)
275                .unwrap_or_else(|| pad(vertices)),
276        }
277    }
278
279    fn skinned_contact_anchors(
280        &self,
281        body: &SoftBody,
282        bindings: &[SoftMeshCellBinding],
283        vertices: &[u32],
284        weights: &[Real],
285        point: Vector,
286    ) -> Option<([u32; DIM + 1], [Real; DIM + 1], Vector)> {
287        let first = bindings.get(*vertices.first()? as usize)?;
288        let shared = vertices
289            .iter()
290            .all(|v| bindings[*v as usize].cell == first.cell);
291
292        if shared {
293            let cell = body.cells.get(first.cell as usize)?;
294            let mut blended = [0.0; DIM + 1];
295            for (v, weight) in vertices.iter().zip(weights) {
296                for (w, bw) in blended.iter_mut().zip(&bindings[*v as usize].weights) {
297                    *w += bw * weight;
298                }
299            }
300            return Some((cell.vertices, blended, point));
301        }
302
303        let heaviest = (0..vertices.len()).max_by(|a, b| weights[*a].total_cmp(&weights[*b]))?;
304        let vertex = vertices[heaviest] as usize;
305        let binding = &bindings[vertex];
306        let cell = body.cells.get(binding.cell as usize)?;
307        Some((cell.vertices, binding.weights, self.vertices[vertex]))
308    }
309
310    /// The rest position of the `i`-th vertex (relative to the body's rest center of mass,
311    /// like `SoftBodyParticle::rest_position`).
312    pub(crate) fn rest_vertex(&self, body: &SoftBody, i: usize) -> Vector {
313        match &self.binding {
314            SoftMeshMapping::Direct { particles } => {
315                body.particles[particles[i] as usize].rest_position
316            }
317            SoftMeshMapping::Skinned { bindings } => {
318                let binding = &bindings[i];
319                let Some(cell) = body.cells.get(binding.cell as usize) else {
320                    return Vector::ZERO;
321                };
322                let mut position = Vector::ZERO;
323                for (vid, weight) in cell.vertices.iter().zip(&binding.weights) {
324                    position += body.particles[*vid as usize].rest_position * *weight;
325                }
326                position
327            }
328        }
329    }
330
331    /// The cell id backing each element, `u32::MAX` for none (empty when the elements are not
332    /// cell-backed at all: wires, skinned meshes).
333    pub(crate) fn element_cell_ids<'a>(&'a self, body: &'a SoftBody) -> &'a [u32] {
334        if self.follows_boundary {
335            &body.boundary_element_cells
336        } else {
337            &self.element_cells
338        }
339    }
340
341    /// The surface elements incident to `vertex`.
342    pub(crate) fn vertex_element_ids(&self, vertex: u32) -> &[u32] {
343        match (
344            self.vertex_elements_offsets.get(vertex as usize),
345            self.vertex_elements_offsets.get(vertex as usize + 1),
346        ) {
347            (Some(&start), Some(&end)) => &self.vertex_elements[start as usize..end as usize],
348            _ => &[],
349        }
350    }
351}