Skip to main content

rapier3d/dynamics/soft_body/soft_body_builder/
soft_body_builder_settings.rs

1//! Setters and topology helpers of the soft-body builder: elements, materials, tearing, colliders and per-body settings.
2
3use crate::alloc_prelude::*;
4use crate::dynamics::SpringCoefficients;
5use crate::geometry::ColliderBuilder;
6use crate::math::{DIM, Real, Vector};
7use parry::utils::hashmap::HashMap;
8#[cfg(not(feature = "std"))]
9#[allow(unused_imports)]
10use simba::scalar::{ComplexField as _, RealField as _};
11
12use super::super::{SoftBodyCellModel, SoftBodyMaterial};
13use super::{SoftBodyBuilder, SoftBodyParticleSettings};
14
15impl SoftBodyBuilder {
16    /*
17     * Setters.
18     */
19
20    /// The particle positions set so far.
21    pub fn particle_positions(&self) -> &[Vector] {
22        &self.positions
23    }
24
25    /// Sets the world-space positions of the particles.
26    pub fn positions(mut self, positions: Vec<Vector>) -> Self {
27        self.positions = positions;
28        self
29    }
30
31    /// Sets the mass of every particle.
32    pub fn particle_mass(mut self, mass: Real) -> Self {
33        self.particle_mass = mass;
34        self.masses.clear();
35        self
36    }
37
38    /// Sets the total mass of the soft body (split uniformly across the particles).
39    pub fn mass(mut self, mass: Real) -> Self {
40        self.particle_mass = mass / self.positions.len().max(1) as Real;
41        self.masses.clear();
42        self
43    }
44
45    /// Sets per-particle masses.
46    pub fn masses(mut self, masses: Vec<Real>) -> Self {
47        self.masses = masses;
48        self
49    }
50
51    /// Pins the given particles (infinite mass, moved kinematically: see
52    /// [`crate::dynamics::SoftBody::set_particle_kinematic_target`]).
53    pub fn pinned_particles(mut self, pinned: impl IntoIterator<Item = u32>) -> Self {
54        self.pinned.extend(pinned);
55        self
56    }
57
58    /// Sets the structural edges.
59    pub fn edges(mut self, edges: Vec<[u32; 2]>) -> Self {
60        self.edges = edges;
61        self
62    }
63
64    /// Adds structural edges (seams between two appended pieces, extra springs...); their
65    /// rest length is the current distance of their particles.
66    pub fn add_edges(mut self, edges: impl IntoIterator<Item = [u32; 2]>) -> Self {
67        let inserted: Vec<[u32; 2]> = edges.into_iter().collect();
68        // The per-edge overrides index the concatenation of `edges` and `bend_edges`: the
69        // bending indices shift.
70        let shift = inserted.len() as u32;
71        let first_bend = self.edges.len() as u32;
72        for i in &mut self.tension_only_edges {
73            if *i >= first_bend {
74                *i += shift;
75            }
76        }
77        for (i, _) in &mut self.edge_softness {
78            if *i >= first_bend {
79                *i += shift;
80            }
81        }
82        for (i, _) in &mut self.edge_tear_resistance {
83            if *i >= first_bend {
84                *i += shift;
85            }
86        }
87        self.edges.extend(inserted);
88        self
89    }
90
91    /// Appends the particles and elements of `other` (its indices shifted past this builder's
92    /// particles): the two pieces become one soft body, to be sewn together with
93    /// [`Self::add_edges`]. This builder's material, model and settings are kept.
94    pub fn append(mut self, other: SoftBodyBuilder) -> Self {
95        let offset = self.positions.len() as u32;
96        // Per-particle masses become explicit as soon as one side has some.
97        if !self.masses.is_empty() || !other.masses.is_empty() {
98            if self.masses.is_empty() {
99                self.masses = vec![self.particle_mass; self.positions.len()];
100            }
101            if other.masses.is_empty() {
102                self.masses.extend(core::iter::repeat_n(
103                    other.particle_mass,
104                    other.positions.len(),
105                ));
106            } else {
107                self.masses.extend_from_slice(&other.masses);
108            }
109        }
110        // The per-edge overrides of both sides, re-indexed into the new concatenation.
111        let (self_edges, self_bends) = (self.edges.len() as u32, self.bend_edges.len() as u32);
112        let (other_edges, other_bends) = (other.edges.len() as u32, other.bend_edges.len() as u32);
113        let remap_self = |i: u32| if i < self_edges { i } else { i + other_edges };
114        let remap_other = |i: u32| {
115            if i < other_edges {
116                i + self_edges
117            } else {
118                i - other_edges + self_edges + other_edges + self_bends
119            }
120        };
121        for i in &mut self.tension_only_edges {
122            *i = remap_self(*i);
123        }
124        for (i, _) in &mut self.edge_softness {
125            *i = remap_self(*i);
126        }
127        for (i, _) in &mut self.edge_tear_resistance {
128            *i = remap_self(*i);
129        }
130        self.tension_only_edges
131            .extend(other.tension_only_edges.iter().map(|&i| remap_other(i)));
132        self.edge_softness.extend(
133            other
134                .edge_softness
135                .iter()
136                .map(|&(i, c)| (remap_other(i), c)),
137        );
138        self.edge_tear_resistance.extend(
139            other
140                .edge_tear_resistance
141                .iter()
142                .map(|&(i, r)| (remap_other(i), r)),
143        );
144        let _ = other_bends;
145        let shift = |v: &[u32]| -> Vec<u32> { v.iter().map(|&i| i + offset).collect() };
146        self.positions.extend_from_slice(&other.positions);
147        self.pinned.extend(shift(&other.pinned));
148        self.edges
149            .extend(other.edges.iter().map(|e| [e[0] + offset, e[1] + offset]));
150        self.bend_edges.extend(
151            other
152                .bend_edges
153                .iter()
154                .map(|e| [e[0] + offset, e[1] + offset]),
155        );
156        #[cfg(feature = "dim3")]
157        self.dihedrals.extend(
158            other
159                .dihedrals
160                .iter()
161                .map(|d| [d[0] + offset, d[1] + offset, d[2] + offset, d[3] + offset]),
162        );
163        self.cells
164            .extend(other.cells.iter().map(|c| c.map(|i| i + offset)));
165        self.surface
166            .extend(other.surface.iter().map(|s| s.map(|i| i + offset)));
167        self
168    }
169
170    /// Sets the bending edges.
171    pub fn bend_edges(mut self, edges: Vec<[u32; 2]>) -> Self {
172        self.bend_edges = edges;
173        self
174    }
175
176    /// Makes every edge (structural and bending) only resist stretching, never compression.
177    pub fn tension_only(mut self) -> Self {
178        self.tension_only_edges = (0..(self.edges.len() + self.bend_edges.len()) as u32).collect();
179        self
180    }
181
182    /// Sets the dihedral bending constraints (shared edge then the two opposite vertices).
183    #[cfg(feature = "dim3")]
184    pub fn dihedrals(mut self, dihedrals: Vec<[u32; 4]>) -> Self {
185        self.dihedrals = dihedrals;
186        self
187    }
188
189    /// Sets the simplex cells (triangles in 2D, tetrahedra in 3D). Structural edges along the
190    /// cell edges are added at build time if none were given.
191    pub fn cells(mut self, cells: Vec<[u32; DIM + 1]>) -> Self {
192        self.cells = cells;
193        self
194    }
195
196    /// Moves everything the builder holds in world space: the particles and the skin.
197    ///
198    /// Useful to drop copies of a meshed body at several places without meshing it again; moving
199    /// the particles alone would leave the skin behind.
200    pub fn translated(mut self, translation: Vector) -> Self {
201        for position in &mut self.positions {
202            *position += translation;
203        }
204        if let Some((vertices, _)) = &mut self.skin {
205            for vertex in vertices {
206                *vertex += translation;
207            }
208        }
209        self
210    }
211
212    /// Sets the mesh the cells hold: the body's default collision mesh is then that mesh, bound to
213    /// the cells ([`crate::dynamics::SoftMeshMapping::Skinned`]) instead of the cells' boundary.
214    /// The vertices are in world space in the cells' build pose, each bound to the closest cell.
215    pub fn skin(mut self, vertices: Vec<Vector>, indices: Vec<[u32; DIM]>) -> Self {
216        self.skin = Some((vertices, indices));
217        self
218    }
219
220    /// Whether the body's skin is its collision mesh rather than its cells' boundary (off by
221    /// default, and no effect without a skin); on, a contact is resolved through the particles of
222    /// the cell holding the touched part of the skin, which must be at least as fine as the cells.
223    pub fn skin_collision(mut self, enabled: bool) -> Self {
224        self.skin_collision = enabled;
225        self
226    }
227
228    /// Sets the segments the body collides through: a rope or a wire, whose collision mesh is a
229    /// polyline rather than a surface (3D only; in 2D [`Self::surface`] already takes segments).
230    /// A wire encloses nothing, so its contacts are two-sided and it never expels anything.
231    #[cfg(feature = "dim3")]
232    pub fn wire(mut self, segments: Vec<[u32; 2]>) -> Self {
233        self.wire = segments;
234        self
235    }
236
237    /// Sets the computational mesh's boundary elements (segments in 2D, triangles in 3D),
238    /// oriented outward: what the volume constraint integrates over, and the geometry of the default
239    /// collision mesh.
240    pub fn surface(mut self, surface: Vec<[u32; DIM]>) -> Self {
241        self.surface = surface;
242        self
243    }
244
245    /// Sets the material.
246    pub fn material(mut self, material: SoftBodyMaterial) -> Self {
247        self.material = material;
248        self
249    }
250
251    /// Sets the same softness for every constraint family.
252    pub fn softness(mut self, softness: SpringCoefficients<Real>) -> Self {
253        self.material = SoftBodyMaterial::uniform(softness);
254        self
255    }
256
257    /// Sets the material's tear strain (see [`SoftBodyMaterial::tear_strain`]).
258    pub fn tear_strain(mut self, strain: Real) -> Self {
259        self.material.tear_strain = Some(strain);
260        self
261    }
262
263    /// Sets the material's tear force (see [`SoftBodyMaterial::tear_force`]).
264    pub fn tear_force(mut self, force: Real) -> Self {
265        self.material.tear_force = Some(force);
266        self
267    }
268
269    /// Sets the smallest piece, in measure elements, a tear may split off (see
270    /// [`SoftBodyMaterial::min_piece`]).
271    pub fn min_piece(mut self, elements: u32) -> Self {
272        self.material.min_piece = Some(elements);
273        self
274    }
275
276    /// Sets the material's tear smoothing time constant (see
277    /// [`SoftBodyMaterial::tear_smoothing`]).
278    pub fn tear_smoothing(mut self, seconds: Real) -> Self {
279        self.material.tear_smoothing = seconds;
280        self
281    }
282
283    /// Sets the material's interior strength (see [`SoftBodyMaterial::interior_strength`]).
284    pub fn interior_strength(mut self, strength: Real) -> Self {
285        self.material.interior_strength = strength;
286        self
287    }
288
289    /// Sets the tear-threshold multiplier of some edges, as `(edge index, resistance)` pairs
290    /// indexing the concatenation of the structural then the bending edges (see
291    /// [`crate::dynamics::SoftBodyEdge::tear_resistance`]).
292    pub fn edge_tear_resistance(mut self, edges: impl IntoIterator<Item = (u32, Real)>) -> Self {
293        self.edge_tear_resistance.extend(edges);
294        self
295    }
296
297    /// Sets the constitutive model of the cells.
298    pub fn cell_model(mut self, model: SoftBodyCellModel) -> Self {
299        self.cell_model = model;
300        self
301    }
302
303    /// Selects the solver simulating the body's elasticity (see [`super::SoftBodySolver`]).
304    #[cfg(feature = "fem")]
305    pub fn solver(mut self, solver: super::SoftBodySolver) -> Self {
306        self.solver = solver;
307        self
308    }
309
310    /// Enables area/volume preservation: one constraint per piece of material enclosed by a
311    /// closed surface (see [`crate::dynamics::SoftBody::volume_pieces`]).
312    pub fn volume_preservation(mut self, enabled: bool) -> Self {
313        self.volume_preservation = enabled;
314        self
315    }
316
317    /// Sets the target volume multiplier (`> 1` inflates the body).
318    pub fn volume_factor(mut self, factor: Real) -> Self {
319        self.volume_factor = factor;
320        self
321    }
322
323    /// Enables shape matching.
324    pub fn shape_matching(mut self, enabled: bool) -> Self {
325        self.shape_matching = enabled;
326        self
327    }
328
329    /// Enables self contacts between the body's surface vertices/edges and its own surface.
330    pub fn self_contacts(mut self, enabled: bool) -> Self {
331        self.self_contacts = enabled;
332        self
333    }
334
335    /// Whether the shape of the body's default collision surface is built with parry's
336    /// `ORIENTED` flag, like a rigid polyline or mesh. Left unset, it is whenever the surface is
337    /// closed, which is what a solid body wants: an oriented closed surface encloses matter, so
338    /// nothing is held inside it. Set it to `false` for a shell, whose inner side holds the bodies
339    /// inside it. After insertion the collider's shape is the authority: change its flags there,
340    /// like for any collider. Independent of the material inside (cells, pressure).
341    pub fn oriented(mut self, oriented: bool) -> Self {
342        self.oriented = Some(oriented);
343        self
344    }
345
346    /// Sets the thickness of the particles (the contact skin of the body's surface collider).
347    pub fn particle_radius(mut self, radius: Real) -> Self {
348        self.particle_radius = radius;
349        self
350    }
351
352    /// Configures the collider of the body's default collision mesh (its cells' boundary, or its
353    /// skin under [`Self::skin_collision`]): friction, restitution, groups, events. The shape is
354    /// the mesh's own; the density is ignored (mass from [`Self::mass`] or [`Self::masses`]).
355    pub fn surface_collider(mut self, collider: ColliderBuilder) -> Self {
356        self.collider_template = Some(collider);
357        self
358    }
359
360    /// Gives the body no default collision mesh collider: it collides through the meshes added
361    /// after insertion, if any.
362    pub fn no_surface_collider(mut self) -> Self {
363        self.collider_template = None;
364        self
365    }
366
367    /// Sets the dynamics settings of the particles (damping, gravity scale, sleeping...).
368    pub fn particle_settings(mut self, settings: SoftBodyParticleSettings) -> Self {
369        self.particle_settings = settings;
370        self
371    }
372
373    /// Sets the linear damping of the particles.
374    pub fn linear_damping(mut self, damping: Real) -> Self {
375        self.particle_settings.linear_damping = damping;
376        self
377    }
378
379    /// Sets the gravity scale of the particles.
380    pub fn gravity_scale(mut self, scale: Real) -> Self {
381        self.particle_settings.gravity_scale = scale;
382        self
383    }
384
385    /// Requests extra solver substeps for the particles (and everything they touch).
386    pub fn additional_solver_iterations(mut self, iterations: usize) -> Self {
387        self.particle_settings.additional_solver_iterations = iterations;
388        self
389    }
390
391    /// Requests extra internal PGS iterations per substep for the particles (and everything
392    /// they touch); see [`SoftBodyParticleSettings::additional_pgs_iterations`].
393    ///
394    /// [`SoftBodyParticleSettings::additional_pgs_iterations`]: super::SoftBodyParticleSettings::additional_pgs_iterations
395    pub fn additional_pgs_iterations(mut self, iterations: usize) -> Self {
396        self.particle_settings.additional_pgs_iterations = iterations;
397        self
398    }
399
400    /// Whether the particles may fall asleep.
401    pub fn can_sleep(mut self, can_sleep: bool) -> Self {
402        self.particle_settings.can_sleep = can_sleep;
403        self
404    }
405
406    /// Sets the user data of the soft body.
407    pub fn user_data(mut self, data: u128) -> Self {
408        self.user_data = data;
409        self
410    }
411
412    /*
413     * Topology helpers.
414     */
415
416    /// The unique edges of the surface elements.
417    pub fn surface_edges(&self) -> Vec<[u32; 2]> {
418        let mut seen: HashMap<[u32; 2], ()> = HashMap::default();
419        let mut edges = Vec::new();
420        for element in &self.surface {
421            for k in 0..DIM {
422                let a = element[k];
423                let b = element[(k + 1) % DIM];
424                let key = [a.min(b), a.max(b)];
425                if seen.insert(key, ()).is_none() {
426                    edges.push(key);
427                }
428            }
429        }
430        edges
431    }
432
433    /// The unique edges of the cells.
434    pub fn cell_edges(&self) -> Vec<[u32; 2]> {
435        let mut seen: HashMap<[u32; 2], ()> = HashMap::default();
436        let mut edges = Vec::new();
437        for cell in &self.cells {
438            for i in 0..DIM + 1 {
439                for j in i + 1..DIM + 1 {
440                    let key = [cell[i].min(cell[j]), cell[i].max(cell[j])];
441                    if seen.insert(key, ()).is_none() {
442                        edges.push(key);
443                    }
444                }
445            }
446        }
447        edges
448    }
449
450    /// The dihedral constraints across every interior edge of the triangle surface (3D only).
451    #[cfg(feature = "dim3")]
452    pub fn surface_dihedrals(&self) -> Vec<[u32; 4]> {
453        // edge -> (first opposite vertex, second opposite vertex)
454        let mut opposite: HashMap<[u32; 2], (u32, Option<u32>)> = HashMap::default();
455        let mut order = Vec::new();
456        for tri in &self.surface {
457            for k in 0..3 {
458                let a = tri[k];
459                let b = tri[(k + 1) % 3];
460                let c = tri[(k + 2) % 3];
461                let key = [a.min(b), a.max(b)];
462                match opposite.get_mut(&key) {
463                    None => {
464                        opposite.insert(key, (c, None));
465                        order.push(key);
466                    }
467                    Some((_, second)) => {
468                        if second.is_none() {
469                            *second = Some(c);
470                        }
471                    }
472                }
473            }
474        }
475        order
476            .into_iter()
477            .filter_map(|key| {
478                let (c, d) = opposite[&key];
479                d.map(|d| [key[0], key[1], c, d])
480            })
481            .collect()
482    }
483}