1use super::soft_body_set_proxies::{rebuilt_surface_shape, sync_soft_body};
3use super::soft_body_set_split::{ProxyPoses, rebase_proxy_attachments};
4use super::{SoftBodyIslandEvent, SoftBodySet};
5use crate::alloc_prelude::*;
6use crate::dynamics::soft_body::{SoftBody, SoftBodyHandle, SoftBodyTearEvent};
7use crate::dynamics::{
8 ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet, RigidBodySet,
9};
10use crate::geometry::{ColliderHandle, ColliderPosition, ColliderSet};
11use crate::math::{DIM, Pose, Real, Vector};
12use crate::pipeline::EventHandler;
13#[cfg(not(feature = "std"))]
14#[allow(unused_imports)]
15use simba::scalar::{ComplexField as _, RealField as _};
16
17impl SoftBodySet {
18 pub(crate) fn apply_user_changes(
22 &mut self,
23 bodies: &mut RigidBodySet,
24 colliders: &mut ColliderSet,
25 params: &IntegrationParameters,
26 quarantined: &mut Vec<SoftBodyHandle>,
27 ) {
28 for (_, sb) in self.bodies.iter_mut() {
30 for mesh in sb.meshes_mut() {
31 mesh.read_orientation(colliders);
32 }
33 }
34 let mut relink = false;
35 let handles: Vec<SoftBodyHandle> = self
36 .bodies
37 .iter()
38 .filter(|(_, sb)| sb.modified || sb.positions_modified || sb.attachments_modified)
39 .map(|(h, _)| SoftBodyHandle(h))
40 .collect();
41 for handle in handles {
42 let sb = &mut self.bodies[handle.0];
43 if core::mem::take(&mut sb.modified) {
44 if sb.enabled && !sb.is_finite() {
45 sb.sanitize_dynamics();
46 sb.enabled = false;
47 quarantined.push(handle);
48 }
49 if let Some(rb) = bodies.get_mut(sb.root_body) {
50 if rb.is_enabled() != sb.enabled {
51 rb.set_enabled(sb.enabled);
52 }
53 if sb.enabled {
54 rb.wake_up(true);
55 }
56 }
57 }
58 if core::mem::take(&mut sb.positions_modified) && sb.enabled {
59 Self::update_colliders(sb, bodies, colliders, false);
60 for mesh in sb.meshes_mut() {
61 mesh.crossing_sweep_travel = Real::MAX;
64 }
65 }
66 if sb.enabled && params.soft_bodies.recovery.authored_velocity_margin {
69 let mut max_speed: Real = 0.0;
70 for p in &sb.particles {
71 max_speed = max_speed.max(p.velocity.length());
72 }
73 let margin = max_speed * params.dt;
74 if margin > 0.0 {
75 for cluster in sb.clusters.iter().filter(|cluster| cluster.is_live()) {
76 if let Some(rb) = bodies.get_mut_internal(cluster.proxy()) {
77 rb.soft_motion_margin = rb.soft_motion_margin.max(margin);
78 }
79 for mesh in cluster.meshes() {
82 let _ = colliders.get_mut(mesh.collider());
83 }
84 }
85 }
86 }
87 if core::mem::take(&mut sb.attachments_modified) {
88 relink = true;
89 self.island_events.push(SoftBodyIslandEvent { handle });
90 }
91 }
92 if relink || self.attached_to_stale {
93 self.attached_to_stale = false;
94 self.attached_to.clear();
95 for (h, sb) in self.bodies.iter() {
96 for (i, a) in sb.attachments.iter().enumerate() {
97 self.attached_to
98 .entry(a.body)
99 .or_default()
100 .push((SoftBodyHandle(h), i as u32));
101 }
102 }
103 }
104 }
105
106 pub(crate) fn update_cluster_proxies(
110 sb: &mut SoftBody,
111 bodies: &mut RigidBodySet,
112 colliders: &mut ColliderSet,
113 ) {
114 let _ = colliders;
115 for ci in 0..sb.clusters.len() {
116 let Some(mut frame) = sb.cluster_frame(ci) else {
117 continue;
118 };
119 if !frame.pose.is_finite() || !frame.linvel.is_finite() || !frame.angvel.is_finite() {
120 continue;
123 }
124 let proxy = sb.clusters[ci].proxy;
125 let Some(rb) = bodies.get_mut_internal(proxy) else {
126 continue;
127 };
128 let prev = rb.pos.position;
131 let dt = (frame.pose.translation - prev.translation).length();
132 #[cfg(feature = "dim2")]
134 let dr = frame.pose.rotation.angle_between(&prev.rotation).abs();
135 #[cfg(feature = "dim3")]
136 let dr = frame.pose.rotation.angle_between(prev.rotation);
137 if dt < 1.0e-6 && dr < 1.0e-6 {
138 frame.pose = prev;
139 }
140 sb.clusters[ci].rotation = frame.pose.rotation;
141 sb.clusters[ci].last_gather = (frame.linvel, frame.angvel);
142 rb.pos.position = frame.pose;
143 rb.pos.next_position = frame.pose;
144 rb.vels.linvel = frame.linvel;
145 rb.vels.angvel = frame.angvel;
146 rb.mprops.local_mprops.inv_mass = frame.inv_mass;
147 rb.mprops.local_mprops.local_com = Vector::ZERO;
148 rb.mprops.world_com = frame.pose.translation;
149 rb.mprops.effective_inv_mass = Vector::splat(frame.inv_mass);
150 rb.mprops.effective_world_inv_inertia = frame.inv_inertia;
151 }
152 }
153
154 pub(super) fn update_colliders(
157 sb: &SoftBody,
158 bodies: &RigidBodySet,
159 colliders: &mut ColliderSet,
160 rebuild: bool,
161 ) {
162 for cluster in sb.clusters.iter().filter(|cluster| cluster.is_live()) {
165 let frame = bodies
166 .get(cluster.proxy())
167 .map(|rb| rb.pos.position)
168 .unwrap_or(Pose::IDENTITY);
169 for mesh in cluster.meshes() {
170 let Some(co) = colliders.get_mut(mesh.collider()) else {
171 continue;
172 };
173 co.deform_pose(frame);
174 let pose = *co.position();
175 if rebuild {
176 match rebuilt_surface_shape(co.shape(), mesh.local_vertices(sb, &pose), mesh) {
177 Some(shape) => co.replace_deformed_shape(shape),
178 None => co.set_enabled(false),
179 }
180 } else {
181 co.deform_shape(|shape| mesh.deform_shape(sb, &pose, shape));
182 }
183 }
184 }
185 }
186
187 pub fn sync_particle_positions(
191 &mut self,
192 bodies: &mut RigidBodySet,
193 colliders: &mut ColliderSet,
194 params: &IntegrationParameters,
195 quarantined: &mut Vec<SoftBodyHandle>,
196 ) {
197 self.sync_particle_positions_and_collect(
198 bodies,
199 colliders,
200 params,
201 params.dt,
202 quarantined,
203 &mut Vec::new(),
204 );
205 }
206
207 pub(crate) fn sync_particle_positions_and_collect(
212 &mut self,
213 bodies: &mut RigidBodySet,
214 colliders: &mut ColliderSet,
215 params: &IntegrationParameters,
216 last_pass_dt: Real,
217 quarantined: &mut Vec<SoftBodyHandle>,
218 synced_colliders: &mut Vec<ColliderHandle>,
219 ) {
220 let mut handles = Vec::with_capacity(self.bodies.len());
224 let mut soft_bodies: Vec<(&mut SoftBody, bool)> = self
225 .bodies
226 .iter_mut()
227 .map(|(h, sb)| {
228 handles.push(SoftBodyHandle(h));
229 let inactive = bodies
230 .get(sb.root_body)
231 .is_none_or(|rb| rb.is_sleeping() || !rb.is_enabled());
232 (sb, inactive)
233 })
234 .collect();
235 let mut outcomes = Vec::with_capacity(soft_bodies.len());
236 let sync = |(sb, inactive): &mut (&mut SoftBody, bool)| {
237 sync_soft_body(sb, *inactive, params, last_pass_dt)
238 };
239 #[cfg(feature = "parallel")]
240 {
241 use rayon::prelude::*;
242 soft_bodies
243 .par_iter_mut()
244 .map(sync)
245 .collect_into_vec(&mut outcomes);
246 }
247 #[cfg(not(feature = "parallel"))]
248 outcomes.extend(soft_bodies.iter_mut().map(sync));
249
250 for (((sb, _), outcome), handle) in soft_bodies.iter_mut().zip(outcomes).zip(handles) {
251 let Some(margin) = outcome.margin else {
252 continue;
253 };
254 if !outcome.finite {
255 sb.sanitize_dynamics();
257 sb.enabled = false;
258 if let Some(rb) = bodies.get_mut(sb.root_body) {
259 rb.set_enabled(false);
260 }
261 quarantined.push(handle);
262 continue;
263 }
264 let Some(rb) = bodies.get_mut_internal(sb.root_body) else {
265 continue;
266 };
267 rb.additional_solver_iterations = outcome.additional_solver_iterations;
268 rb.additional_pgs_iterations = sb.particle_settings.additional_pgs_iterations;
269 Self::update_cluster_proxies(sb, bodies, colliders);
272 for cluster in sb.clusters.iter().filter(|cluster| cluster.is_live()) {
273 let frame = bodies
274 .get(cluster.proxy())
275 .map(|rb| rb.pos.position)
276 .unwrap_or(Pose::IDENTITY);
277 for mesh in cluster.meshes() {
278 if let Some(co) = colliders.get_mut(mesh.collider()) {
279 co.deform_pose(frame);
281 let pose = *co.position();
282 co.deform_shape(|shape| mesh.deform_shape(sb, &pose, shape));
283 synced_colliders.push(mesh.collider());
284 }
285 }
286 let Some(rb) = bodies.get_mut_internal(cluster.proxy()) else {
289 continue;
290 };
291 rb.soft_motion_margin = margin;
292 let proxy_pose = rb.pos.position;
293 for handle in rb.colliders() {
295 let Some(co) = colliders.get_mut(*handle) else {
296 continue;
297 };
298 if !co.is_deformable_collider() {
301 if let Some(parent) = co.parent.as_ref() {
302 co.pos = ColliderPosition(proxy_pose * parent.pos_wrt_parent);
303 synced_colliders.push(*handle);
304 }
305 }
306 }
307 }
308 }
309 }
310
311 pub(crate) fn apply_pending_tears(
315 &mut self,
316 islands: &mut IslandManager,
317 bodies: &mut RigidBodySet,
318 colliders: &mut ColliderSet,
319 impulse_joints: &mut ImpulseJointSet,
320 multibody_joints: &mut MultibodyJointSet,
321 events: &dyn EventHandler,
322 ) {
323 let pending: Vec<SoftBodyHandle> = self
324 .bodies
325 .iter()
326 .filter(|(_, sb)| sb.tearing_pending)
327 .map(|(h, _)| SoftBodyHandle(h))
328 .collect();
329 let mut torn = Vec::new();
330 for handle in pending {
331 let (edges, cells) = self.bodies[handle.0].take_torn();
332 if let Some(event) = self.tear(
333 handle,
334 &edges,
335 &cells,
336 islands,
337 bodies,
338 colliders,
339 impulse_joints,
340 multibody_joints,
341 ) {
342 torn.push(event);
343 }
344 }
345 for event in &torn {
346 events.handle_soft_body_tear_event(self, event);
347 }
348 }
349
350 #[allow(clippy::too_many_arguments)]
354 pub fn tear(
355 &mut self,
356 handle: SoftBodyHandle,
357 edges: &[u32],
358 cells: &[u32],
359 islands: &mut IslandManager,
360 bodies: &mut RigidBodySet,
361 colliders: &mut ColliderSet,
362 impulse_joints: &mut ImpulseJointSet,
363 multibody_joints: &mut MultibodyJointSet,
364 ) -> Option<SoftBodyTearEvent> {
365 let _ = multibody_joints;
366 self.change_topology(
367 handle,
368 islands,
369 bodies,
370 colliders,
371 impulse_joints,
372 |sb, event| sb.tear_topology(edges, cells, event),
373 )
374 }
375
376 fn change_topology(
380 &mut self,
381 handle: SoftBodyHandle,
382 islands: &mut IslandManager,
383 bodies: &mut RigidBodySet,
384 colliders: &mut ColliderSet,
385 impulse_joints: &mut ImpulseJointSet,
386 change: impl FnOnce(&mut SoftBody, &mut SoftBodyTearEvent) -> bool,
387 ) -> Option<SoftBodyTearEvent> {
388 let sb = self.bodies.get_mut(handle.0)?;
389 let mut event = SoftBodyTearEvent {
390 soft_body: handle,
391 ..Default::default()
392 };
393 if !change(sb, &mut event) {
394 return None;
395 }
396
397 let seeds = event.seeds();
400 let mut poses = ProxyPoses::default();
401 let split = self.split_clusters_along(
402 handle,
403 &seeds,
404 islands,
405 bodies,
406 colliders,
407 impulse_joints,
408 &mut event,
409 &mut poses,
410 );
411 let mut handles = self.split_body_along(
412 handle,
413 &seeds,
414 islands,
415 bodies,
416 colliders,
417 impulse_joints,
418 &mut event,
419 &mut poses,
420 );
421 let split = split || !handles.is_empty();
422 handles.insert(0, handle);
423
424 for &piece in &handles {
425 let sb = &mut self.bodies[piece.0];
426 if split {
427 Self::update_cluster_proxies(sb, bodies, colliders);
429 }
430 Self::update_colliders(sb, bodies, colliders, true);
433 }
434 if split {
435 rebase_proxy_attachments(&poses, bodies, colliders, impulse_joints);
436 }
437 for &piece in &handles {
438 if let Some(rb) = bodies.get_mut(self.bodies[piece.0].root_body) {
439 rb.wake_up(true);
440 }
441 }
442 Some(event)
443 }
444
445 #[allow(clippy::too_many_arguments)]
449 pub fn cut(
450 &mut self,
451 handle: SoftBodyHandle,
452 blade: &[Vector; DIM],
453 islands: &mut IslandManager,
454 bodies: &mut RigidBodySet,
455 colliders: &mut ColliderSet,
456 impulse_joints: &mut ImpulseJointSet,
457 multibody_joints: &mut MultibodyJointSet,
458 ) -> Option<SoftBodyTearEvent> {
459 let _ = multibody_joints;
460 self.change_topology(
461 handle,
462 islands,
463 bodies,
464 colliders,
465 impulse_joints,
466 |sb, event| sb.cut_topology(blade, event),
467 )
468 }
469
470 pub(crate) fn refresh_vertex_caches(&mut self) {
473 #[cfg(feature = "parallel")]
474 {
475 use rayon::prelude::*;
476 let mut bodies: Vec<&mut SoftBody> = self.bodies.iter_mut().map(|(_, sb)| sb).collect();
477 bodies
478 .par_iter_mut()
479 .for_each(|sb| sb.refresh_vertex_caches());
480 }
481 #[cfg(not(feature = "parallel"))]
482 for (_, sb) in self.bodies.iter_mut() {
483 sb.refresh_vertex_caches();
484 }
485 }
486
487 pub fn wake_up(&mut self, handle: SoftBodyHandle, bodies: &mut RigidBodySet, strong: bool) {
489 if let Some(sb) = self.bodies.get_mut(handle.0) {
490 sb.wake_up();
491 if let Some(rb) = bodies.get_mut(sb.root_body) {
492 rb.wake_up(strong);
493 }
494 }
495 }
496}