Skip to main content

rapier3d/dynamics/soft_body/
soft_body_meshes.rs

1//! The collision meshes of a soft body: lookup, binding to clusters, insertion and removal, and update after a step or a tear.
2use super::{SoftBody, SoftBodyCell, SoftCollisionMesh, SoftMeshId};
3use crate::alloc_prelude::*;
4use crate::math::{DIM, Vector};
5
6impl SoftBody {
7    /// The meshes this soft body meets the world through, over all its clusters (and the ones
8    /// it is only drawn as: a mesh whose collision is disabled has no collider).
9    pub fn meshes(&self) -> impl Iterator<Item = &SoftCollisionMesh> {
10        self.clusters
11            .iter()
12            .filter(|cluster| cluster.is_live())
13            .flat_map(|cluster| cluster.meshes())
14    }
15
16    /// The mesh with the given id, if it is live.
17    pub fn mesh(&self, id: SoftMeshId) -> Option<&SoftCollisionMesh> {
18        self.clusters
19            .get(id.cluster as usize)
20            .filter(|cluster| cluster.is_live())
21            .and_then(|cluster| cluster.mesh(id.mesh))
22    }
23
24    /// The mesh held by `collider`, if this body owns it.
25    pub fn mesh_of(&self, collider: crate::geometry::ColliderHandle) -> Option<&SoftCollisionMesh> {
26        self.meshes().find(|mesh| mesh.collider() == collider)
27    }
28
29    /// The first mesh this soft body collides through, if any.
30    pub fn collision_mesh(&self) -> Option<&SoftCollisionMesh> {
31        self.meshes().find(|mesh| mesh.collision_enabled())
32    }
33
34    /// The mesh with the given id, mutably.
35    /// Iterates over all collision meshes mutably.
36    pub(crate) fn meshes_mut(&mut self) -> impl Iterator<Item = &mut SoftCollisionMesh> {
37        self.clusters.iter_mut().flat_map(|c| c.meshes_mut())
38    }
39
40    /// Refreshes every mesh's vertex cache from the particles (see
41    /// `SoftCollisionMesh::cached_vertex`).
42    pub(crate) fn refresh_vertex_caches(&mut self) {
43        let particles = &self.particles;
44        for cluster in &mut self.clusters {
45            for mesh in cluster.meshes_mut() {
46                mesh.refresh_vertex_cache(particles);
47            }
48        }
49    }
50
51    pub(crate) fn mesh_mut(&mut self, id: SoftMeshId) -> Option<&mut SoftCollisionMesh> {
52        self.clusters
53            .get_mut(id.cluster as usize)?
54            .meshes
55            .get_mut(id.mesh as usize)?
56            .as_mut()
57    }
58
59    /// The mesh held by `collider`, mutably.
60    pub(crate) fn mesh_of_mut(
61        &mut self,
62        collider: crate::geometry::ColliderHandle,
63    ) -> Option<&mut SoftCollisionMesh> {
64        self.clusters
65            .iter_mut()
66            .flat_map(|cluster| cluster.meshes.iter_mut().flatten())
67            .find(|mesh| mesh.collider() == collider)
68    }
69
70    /// Binds a mesh to the cluster `cluster`, without inserting it: the geometry is given in
71    /// world space, as it sits when the binding is taken.
72    pub(crate) fn bind_mesh(
73        &self,
74        cluster: u32,
75        binding: &super::SoftMeshBinding,
76        vertices: Vec<Vector>,
77        indices: Vec<[u32; DIM]>,
78    ) -> Result<SoftCollisionMesh, super::SoftBindingError> {
79        use super::{SoftBindingError, SoftMeshBindingMode, SoftMeshMapping};
80        let cluster = self
81            .cluster(cluster)
82            .ok_or(SoftBindingError::NotAClusterProxy)?;
83        let in_cluster = |particle: u32| cluster.particles().binary_search(&particle).is_ok();
84
85        let mapping = match &binding.mode {
86            SoftMeshBindingMode::Direct { particles } => {
87                if particles.len() != vertices.len() {
88                    return Err(SoftBindingError::DegenerateMesh);
89                }
90                for (vertex, &particle) in particles.iter().enumerate() {
91                    if particle as usize >= self.particles.len() || !in_cluster(particle) {
92                        return Err(SoftBindingError::VertexOutsideCluster {
93                            vertex: vertex as u32,
94                            particle,
95                        });
96                    }
97                }
98                SoftMeshMapping::Direct {
99                    particles: particles.clone(),
100                }
101            }
102            SoftMeshBindingMode::DirectByPosition { eps } => {
103                let mut particles = Vec::with_capacity(vertices.len());
104                for (vertex, position) in vertices.iter().enumerate() {
105                    let closest = cluster
106                        .particles()
107                        .iter()
108                        .map(|&p| {
109                            (
110                                p,
111                                (self.particles[p as usize].position - *position).length(),
112                            )
113                        })
114                        .filter(|(_, d)| *d <= *eps)
115                        .min_by(|a, b| a.1.total_cmp(&b.1));
116                    let (particle, _) = closest.ok_or(SoftBindingError::UnmatchedVertex {
117                        vertex: vertex as u32,
118                    })?;
119                    particles.push(particle);
120                }
121                SoftMeshMapping::Direct { particles }
122            }
123            SoftMeshBindingMode::Skinned => {
124                // Only the cells the cluster fully owns may hold the mesh: a vertex must not
125                // move particles its cluster does not have.
126                let cells: Vec<usize> = (0..self.cells.len())
127                    .filter(|&c| self.cells[c].vertices.iter().all(|v| in_cluster(*v)))
128                    .collect();
129                let owned: Vec<SoftBodyCell> = cells.iter().map(|&c| self.cells[c]).collect();
130                // The mesh is given in world space, so it binds against where the particles
131                // are now (`rest_position` is relative to the rest center of mass).
132                let positions: Vec<Vector> = self.particles.iter().map(|p| p.position).collect();
133                let bindings =
134                    super::collision_mesh::bind_to_cells(&vertices, &indices, &owned, &positions)
135                        .ok_or(SoftBindingError::UnboundVertex { vertex: 0 })?;
136                // `bind_to_cells` indexes the cells it was given: back to the body's own ids.
137                SoftMeshMapping::Skinned {
138                    bindings: bindings
139                        .into_iter()
140                        .map(|mut binding| {
141                            binding.cell = cells[binding.cell as usize] as u32;
142                            binding
143                        })
144                        .collect(),
145                }
146            }
147        };
148
149        Ok(SoftCollisionMesh::new(
150            mapping,
151            indices,
152            vertices,
153            self,
154            binding.self_contacts,
155        ))
156    }
157
158    /// Puts a bound mesh in its cluster, reusing a dead slot when there is one.
159    pub(crate) fn push_mesh(&mut self, cluster: u32, mut mesh: SoftCollisionMesh) -> SoftMeshId {
160        let meshes = &mut self.clusters[cluster as usize].meshes;
161        let slot = meshes
162            .iter()
163            .position(|slot| slot.is_none())
164            .unwrap_or(meshes.len());
165        let id = SoftMeshId {
166            cluster,
167            mesh: slot as u32,
168        };
169        mesh.set_id(id);
170        if slot == meshes.len() {
171            meshes.push(Some(mesh));
172        } else {
173            meshes[slot] = Some(mesh);
174        }
175        self.modified = true;
176        id
177    }
178
179    /// Records the collider a freshly inserted mesh got.
180    pub(crate) fn set_mesh_collider(
181        &mut self,
182        id: SoftMeshId,
183        collider: crate::geometry::ColliderHandle,
184    ) {
185        if let Some(mesh) = self.mesh_mut(id) {
186            mesh.collider = collider;
187        }
188    }
189
190    /// Removes the mesh `id` from its cluster, leaving a dead slot.
191    pub(crate) fn remove_mesh(&mut self, id: SoftMeshId) -> Option<SoftCollisionMesh> {
192        self.clusters
193            .get_mut(id.cluster as usize)?
194            .meshes
195            .get_mut(id.mesh as usize)?
196            .take()
197    }
198
199    /// Runs `f` on every mesh of this body, with the body itself in hand.
200    pub(crate) fn for_each_mesh_mut(
201        &mut self,
202        mut f: impl FnMut(&SoftBody, &mut SoftCollisionMesh),
203    ) {
204        let mut clusters = core::mem::take(&mut self.clusters);
205        for cluster in &mut clusters {
206            for mesh in cluster.meshes.iter_mut().flatten() {
207                f(self, mesh);
208            }
209        }
210        self.clusters = clusters;
211    }
212
213    /// Recomputes which particles are vertices of the surface.
214    pub(crate) fn update_surface_flags(&mut self) {
215        for p in &mut self.particles {
216            p.on_surface = false;
217        }
218        for element in &self.boundary {
219            for &v in element {
220                if let Some(p) = self.particles.get_mut(v as usize) {
221                    p.on_surface = true;
222                }
223            }
224        }
225    }
226
227    /// Updates the meshes' cached vertices from the current particle positions.
228    pub(crate) fn update_meshes(&mut self) {
229        self.for_each_mesh_mut(|body, mesh| mesh.update(&body.cells, &body.particles));
230    }
231
232    /// Rebuilds the meshes' derived tables after a topology change (a tear).
233    pub(crate) fn rebuild_mesh_tables(&mut self) {
234        self.for_each_mesh_mut(|body, mesh| mesh.rebuild_tables(body));
235    }
236}