1use crate::alloc_prelude::*;
5use crate::geometry::ColliderHandle;
6use crate::math::{DIM, Pose, 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::collision_mesh_binding::{bind_to_cells, closest_cell_binding};
13use super::soft_body_builder::{element_vertices, element_vertices_mut};
14use super::{
15 SoftBody, SoftBodyCell, SoftCollisionMesh, SoftMeshCellBinding, SoftMeshId, SoftMeshMapping,
16 SoftTopologyRemap, surface_element_cells, surface_is_closed, surface_rings,
17 surface_vertex_elements,
18};
19
20impl SoftCollisionMesh {
21 pub(crate) fn boundary(body: &SoftBody, self_contacts: bool, collision_enabled: bool) -> Self {
25 let num_vertices = body.particles.len();
26 let indices = body.boundary.clone();
27 let (ring_offsets, ring) = surface_rings(num_vertices, &indices);
28 let (vertex_elements_offsets, vertex_elements) =
29 surface_vertex_elements(num_vertices, &indices);
30 #[cfg(feature = "dim3")]
31 let (edges, element_edges, edge_owners) =
32 super::soft_body_builder::surface_edge_table(&indices);
33
34 Self {
35 binding: SoftMeshMapping::Direct {
36 particles: (0..num_vertices as u32).collect(),
37 },
38 id: SoftMeshId {
39 cluster: 0,
40 mesh: 0,
41 },
42 follows_boundary: true,
43 closed: surface_is_closed(&indices),
44 rest_signed_volume: SoftBody::boundary_volume(&indices, |i| {
45 body.particles[i as usize].rest_position
46 }),
47 element_cells: Vec::new(),
48 indices,
49 vertices: Vec::new(),
50 ring_offsets,
51 ring,
52 vertex_elements_offsets,
53 vertex_elements,
54 #[cfg(feature = "dim3")]
55 edges,
56 #[cfg(feature = "dim3")]
57 element_edges,
58 #[cfg(feature = "dim3")]
59 edge_owners,
60 inverted: false,
61 orientation_unreliable: false,
62 oriented: false,
63 collision_enabled,
64 collider: ColliderHandle::invalid(),
65 self_contacts,
66 edge_contacts: Vec::new(),
67 vertex_contacts: Vec::new(),
68 overlap_states: Vec::new(),
69 overlap_warm: Vec::new(),
70 volume_contacts: Vec::new(),
71 crossing_sweep_travel: Real::MAX,
72 crossed_partners: Vec::new(),
73 vertex_cache: Vec::new(),
74 topology_version: 0,
75 }
76 }
77
78 pub(crate) fn new(
81 mapping: SoftMeshMapping,
82 indices: Vec<[u32; DIM]>,
83 vertices: Vec<Vector>,
84 body: &SoftBody,
85 self_contacts: bool,
86 ) -> Self {
87 let num_vertices = vertices.len();
88 let (ring_offsets, ring) = surface_rings(num_vertices, &indices);
89 let (vertex_elements_offsets, vertex_elements) =
90 surface_vertex_elements(num_vertices, &indices);
91 #[cfg(feature = "dim3")]
92 let (edges, element_edges, edge_owners) =
93 super::soft_body_builder::surface_edge_table(&indices);
94 let skinned = matches!(mapping, SoftMeshMapping::Skinned { .. });
95 let wire = indices
97 .first()
98 .is_some_and(|e| super::soft_body_builder::element_vertices(e).len() < DIM);
99
100 Self {
101 closed: surface_is_closed(&indices),
102 rest_signed_volume: if wire {
104 0.0
105 } else {
106 SoftBody::boundary_volume(&indices, |i| vertices[i as usize])
107 },
108 element_cells: if skinned || wire {
109 Vec::new()
110 } else {
111 surface_element_cells(&indices, &body.cells)
112 },
113 binding: mapping,
114 id: SoftMeshId::default(),
115 follows_boundary: false,
116 indices,
117 vertices: if skinned { vertices } else { Vec::new() },
119 ring_offsets,
120 ring,
121 vertex_elements_offsets,
122 vertex_elements,
123 #[cfg(feature = "dim3")]
124 edges,
125 #[cfg(feature = "dim3")]
126 element_edges,
127 #[cfg(feature = "dim3")]
128 edge_owners,
129 inverted: false,
130 orientation_unreliable: false,
131 oriented: false,
132 collision_enabled: true,
133 collider: ColliderHandle::invalid(),
134 self_contacts,
135 edge_contacts: Vec::new(),
136 vertex_contacts: Vec::new(),
137 overlap_states: Vec::new(),
138 overlap_warm: Vec::new(),
139 volume_contacts: Vec::new(),
140 crossing_sweep_travel: Real::MAX,
141 crossed_partners: Vec::new(),
142 vertex_cache: Vec::new(),
143 topology_version: 0,
144 }
145 }
146
147 #[cfg(feature = "dim3")]
151 pub(crate) fn wire(
152 body: &SoftBody,
153 segments: &[[u32; 2]],
154 self_contacts: bool,
155 collision_enabled: bool,
156 ) -> Self {
157 let indices: Vec<[u32; DIM]> = segments
158 .iter()
159 .map(|s| core::array::from_fn(|k| if k < 2 { s[k] } else { u32::MAX }))
160 .collect();
161 let mut mesh = Self::new(
162 SoftMeshMapping::Direct {
163 particles: (0..body.particles.len() as u32).collect(),
164 },
165 indices,
166 body.particles.iter().map(|p| p.position).collect(),
167 body,
168 self_contacts,
169 );
170 mesh.collision_enabled = collision_enabled;
171 mesh
172 }
173
174 pub(crate) fn skinned(
178 vertices: &[Vector],
179 indices: &[[u32; DIM]],
180 cells: &[SoftBodyCell],
181 positions: &[Vector],
182 self_contacts: bool,
183 collision_enabled: bool,
184 ) -> Option<Self> {
185 let bindings = bind_to_cells(vertices, indices, cells, positions)?;
186 let (ring_offsets, ring) = surface_rings(vertices.len(), indices);
187 let (vertex_elements_offsets, vertex_elements) =
188 surface_vertex_elements(vertices.len(), indices);
189 #[cfg(feature = "dim3")]
190 let (edges, element_edges, edge_owners) =
191 super::soft_body_builder::surface_edge_table(indices);
192
193 Some(Self {
194 binding: SoftMeshMapping::Skinned { bindings },
195 id: SoftMeshId {
196 cluster: 0,
197 mesh: 0,
198 },
199 follows_boundary: false,
200 closed: surface_is_closed(indices),
201 rest_signed_volume: SoftBody::boundary_volume(indices, |i| vertices[i as usize]),
202 element_cells: Vec::new(),
203 indices: indices.to_vec(),
204 vertices: vertices.to_vec(),
205 ring_offsets,
206 ring,
207 vertex_elements_offsets,
208 vertex_elements,
209 #[cfg(feature = "dim3")]
210 edges,
211 #[cfg(feature = "dim3")]
212 element_edges,
213 #[cfg(feature = "dim3")]
214 edge_owners,
215 inverted: false,
216 orientation_unreliable: false,
217 oriented: false,
218 collision_enabled,
219 collider: ColliderHandle::invalid(),
220 self_contacts,
221 edge_contacts: Vec::new(),
222 vertex_contacts: Vec::new(),
223 overlap_states: Vec::new(),
224 overlap_warm: Vec::new(),
225 volume_contacts: Vec::new(),
226 crossing_sweep_travel: Real::MAX,
227 crossed_partners: Vec::new(),
228 vertex_cache: Vec::new(),
229 topology_version: 0,
230 })
231 }
232
233 pub(crate) fn local_vertices(&self, body: &SoftBody, pose: &Pose) -> Vec<Vector> {
237 (0..self.vertex_count())
238 .map(|i| pose.inverse_transform_point(self.vertex(body, i)))
239 .collect()
240 }
241
242 pub(crate) fn deform_shape(
245 &self,
246 body: &SoftBody,
247 pose: &Pose,
248 shape: &mut dyn parry::shape::Shape,
249 ) {
250 let write = |vertices: &mut [Vector]| {
251 debug_assert_eq!(vertices.len(), self.vertex_count());
252 for (i, v) in vertices.iter_mut().enumerate() {
253 *v = pose.inverse_transform_point(self.vertex(body, i));
254 }
255 };
256 #[cfg(feature = "dim3")]
257 if let Some(trimesh) = shape.as_trimesh_mut() {
258 trimesh.update_vertices(write);
259 return;
260 }
261 if let Some(polyline) = shape.as_polyline_mut() {
263 polyline.update_vertices(write);
264 }
265 }
266
267 pub(crate) fn update(&mut self, cells: &[SoftBodyCell], particles: &[super::SoftBodyParticle]) {
270 let SoftMeshMapping::Skinned { bindings } = &self.binding else {
271 return;
272 };
273 for (vertex, binding) in self.vertices.iter_mut().zip(bindings) {
274 let Some(cell) = cells.get(binding.cell as usize) else {
275 continue;
276 };
277 let mut skinned = Vector::ZERO;
278 for (vid, weight) in cell.vertices.iter().zip(&binding.weights) {
279 skinned += particles[*vid as usize].position * *weight;
280 }
281 *vertex = skinned;
282 }
283 }
284
285 pub(crate) fn update_orientation(&mut self, body: &SoftBody) {
287 if !self.closed || self.rest_signed_volume == 0.0 {
288 return;
289 }
290 let volume = SoftBody::boundary_volume(&self.indices, |i| self.vertex(body, i as usize));
293 let ratio = volume / self.rest_signed_volume;
294 if ratio < -0.25 {
295 self.inverted = true;
296 } else if ratio > 0.25 {
297 self.inverted = false;
298 }
299 self.orientation_unreliable = ratio.abs() <= 0.25;
302 }
303
304 pub fn topology_version(&self) -> u32 {
307 self.topology_version
308 }
309
310 pub(crate) fn rebuild_tables(&mut self, body: &SoftBody) {
312 self.topology_version = self.topology_version.wrapping_add(1);
313 let num_vertices = self.vertex_count();
314 let (offsets, ring) = surface_rings(num_vertices, &self.indices);
315 self.ring_offsets = offsets;
316 self.ring = ring;
317 let (offsets, elements) = surface_vertex_elements(num_vertices, &self.indices);
318 self.vertex_elements_offsets = offsets;
319 self.vertex_elements = elements;
320 self.closed = surface_is_closed(&self.indices);
321 if !self.is_skinned() && !self.follows_boundary {
322 self.element_cells = surface_element_cells(&self.indices, &body.cells);
323 }
324 #[cfg(feature = "dim3")]
325 {
326 let (edges, element_edges, owners) =
327 super::soft_body_builder::surface_edge_table(&self.indices);
328 self.edges = edges;
329 self.element_edges = element_edges;
330 self.edge_owners = owners;
331 }
332 }
333
334 pub(crate) fn remap_topology(&mut self, body: &SoftBody, remap: &SoftTopologyRemap) {
338 match &mut self.binding {
339 SoftMeshMapping::Skinned { bindings } => {
340 if !remap.cells.is_empty() {
341 for binding in bindings.iter_mut() {
342 binding.cell = remap
343 .cells
344 .get(binding.cell as usize)
345 .copied()
346 .unwrap_or(u32::MAX);
347 }
348 }
349 let dangling: Vec<usize> = bindings
350 .iter()
351 .enumerate()
352 .filter(|(_, binding)| body.cells.get(binding.cell as usize).is_none())
353 .map(|(i, _)| i)
354 .collect();
355 for vertex in dangling {
356 let position = self.vertices[vertex];
357 if let Some(binding) = closest_cell_binding(body, position, |_| true) {
358 bindings[vertex] = binding;
359 }
360 }
361 if !remap.split.is_empty() {
362 follow_pieces(bindings, &mut self.vertices, &mut self.indices, body);
363 }
364 }
365 SoftMeshMapping::Direct { particles } if self.follows_boundary => {
366 self.indices = body.boundary.clone();
367 *particles = (0..body.particles.len() as u32).collect();
368 self.rest_signed_volume = SoftBody::boundary_volume(&self.indices, |i| {
369 body.particles[i as usize].rest_position
370 });
371 }
372 SoftMeshMapping::Direct { particles } => {
373 if !remap.particles.is_empty() {
374 let mut vertex_remap = vec![u32::MAX; particles.len()];
377 let mut kept = Vec::with_capacity(particles.len());
378 for (vertex, particle) in particles.iter().enumerate() {
379 let new = remap.particles.get(*particle as usize).copied();
380 if let Some(new) = new.filter(|p| *p != u32::MAX) {
381 vertex_remap[vertex] = kept.len() as u32;
382 kept.push(new);
383 }
384 }
385 *particles = kept;
386 self.indices.retain(|element| {
389 element_vertices(element)
390 .iter()
391 .all(|v| vertex_remap[*v as usize] != u32::MAX)
392 });
393 for element in &mut self.indices {
394 for v in element_vertices_mut(element) {
395 *v = vertex_remap[*v as usize];
396 }
397 }
398 }
399 if !remap.inserted.is_empty() {
400 follow_insertions(particles, &mut self.indices, remap.inserted);
401 }
402 if !remap.split.is_empty() {
403 follow_splits(particles, &mut self.indices, body, remap.split);
404 }
405 }
406 }
407 }
408
409 pub(crate) fn restricted_to(
413 &self,
414 body: &SoftBody,
415 keep: impl Fn(u32) -> bool,
416 ) -> Option<Self> {
417 let n = self.vertex_count();
418 let mut vertex_remap = vec![u32::MAX; n];
419 let mut kept = 0u32;
420 for v in 0..n {
421 if keep(v as u32) {
422 vertex_remap[v] = kept;
423 kept += 1;
424 }
425 }
426 let indices: Vec<[u32; DIM]> = self
427 .indices
428 .iter()
429 .filter(|element| {
430 element_vertices(element)
431 .iter()
432 .all(|v| vertex_remap[*v as usize] != u32::MAX)
433 })
434 .map(|element| {
435 let mut element = *element;
436 for v in element_vertices_mut(&mut element) {
437 *v = vertex_remap[*v as usize];
438 }
439 element
440 })
441 .collect();
442 if indices.is_empty() {
443 return None;
444 }
445 let mut mesh = self.clone();
446 mesh.indices = indices;
447 let mut keep = (0..n).map(|v| vertex_remap[v] != u32::MAX);
448 match &mut mesh.binding {
449 SoftMeshMapping::Direct { particles } => particles.retain(|_| keep.next().unwrap()),
450 SoftMeshMapping::Skinned { bindings } => {
451 let mut keep_vertices = keep.clone();
452 bindings.retain(|_| keep.next().unwrap());
453 mesh.vertices.retain(|_| keep_vertices.next().unwrap());
454 }
455 }
456 mesh.collider = ColliderHandle::invalid();
457 mesh.id = SoftMeshId::default();
458 mesh.clear_contacts();
459 mesh.overlap_states.clear();
460 mesh.overlap_warm.clear();
461 mesh.volume_contacts.clear();
462 mesh.rebuild_tables(body);
463 mesh.rest_signed_volume = if mesh.is_wire() {
464 0.0
465 } else {
466 SoftBody::boundary_volume(&mesh.indices, |i| mesh.rest_vertex(body, i as usize))
467 };
468 mesh.update_orientation(body);
469 Some(mesh)
470 }
471
472 pub(crate) fn clear_contacts(&mut self) {
474 self.edge_contacts.clear();
475 self.vertex_contacts.clear();
476 self.crossing_sweep_travel = Real::MAX;
477 self.crossed_partners.clear();
478 }
479}
480
481fn follow_insertions(
485 particles: &mut Vec<u32>,
486 indices: &mut Vec<[u32; DIM]>,
487 inserted: &[[u32; 4]],
488) {
489 for &[a, b, p, q] in inserted {
490 for i in 0..indices.len() {
491 let element = indices[i];
492 if element_vertices(&element).len() != 2 {
493 continue;
494 }
495 let ends = [
496 particles[element[0] as usize],
497 particles[element[1] as usize],
498 ];
499 let (first, second) = if ends == [a, b] {
500 (p, q)
501 } else if ends == [b, a] {
502 (q, p)
503 } else {
504 continue;
505 };
506 particles.push(first);
507 particles.push(second);
508 let n = particles.len() as u32;
509 indices[i][1] = n - 2;
510 let mut rest = element;
511 rest[0] = n - 1;
512 indices.push(rest);
513 }
514 }
515}
516
517fn follow_pieces(
521 bindings: &mut Vec<SoftMeshCellBinding>,
522 vertices: &mut Vec<Vector>,
523 indices: &mut [[u32; DIM]],
524 body: &SoftBody,
525) {
526 let pieces = body.connected_pieces();
527 if pieces.len() < 2 {
528 return;
529 }
530 let mut particle_piece = vec![u32::MAX; body.particles.len()];
531 for (k, piece) in pieces.iter().enumerate() {
532 for &p in piece {
533 particle_piece[p as usize] = k as u32;
534 }
535 }
536 let cell_piece = |cell: u32| {
537 body.cells
538 .get(cell as usize)
539 .map_or(u32::MAX, |c| particle_piece[c.vertices[0] as usize])
540 };
541 let mut copies: HashMap<(u32, u32), u32> = HashMap::default();
542 for element in indices.iter_mut() {
543 let used = element_vertices(element).len();
544 let piece_of: Vec<u32> = element[..used]
545 .iter()
546 .map(|&w| {
547 bindings
548 .get(w as usize)
549 .map_or(u32::MAX, |b| cell_piece(b.cell))
550 })
551 .collect();
552 let mut majority = (u32::MAX, 0);
554 for &piece in piece_of.iter().filter(|&&piece| piece != u32::MAX) {
555 let count = piece_of.iter().filter(|&&other| other == piece).count();
556 if count > majority.1 {
557 majority = (piece, count);
558 }
559 }
560 let majority = majority.0;
561 if piece_of
562 .iter()
563 .all(|&piece| piece == majority || piece == u32::MAX)
564 {
565 continue;
566 }
567 for k in 0..used {
568 if piece_of[k] == majority || piece_of[k] == u32::MAX {
569 continue;
570 }
571 let w = element[k];
572 let copy = match copies.get(&(w, majority)) {
573 Some(©) => copy,
574 None => {
575 let position = vertices[w as usize];
576 let Some(binding) =
577 closest_cell_binding(body, position, |cell| cell_piece(cell) == majority)
578 else {
579 continue;
580 };
581 bindings.push(binding);
582 vertices.push(position);
583 let copy = vertices.len() as u32 - 1;
584 let _ = copies.insert((w, majority), copy);
585 copy
586 }
587 };
588 element[k] = copy;
589 }
590 }
591}
592
593const MAX_SPLIT_COMBINATIONS: usize = 64;
595
596fn follow_splits(
600 particles: &mut Vec<u32>,
601 indices: &mut [[u32; DIM]],
602 body: &SoftBody,
603 split: &[(u32, u32)],
604) {
605 let mut root: HashMap<u32, u32> = HashMap::default();
607 let mut families: HashMap<u32, Vec<u32>> = HashMap::default();
608 for &(copy, source) in split {
609 let first = root.get(&source).copied().unwrap_or(source);
610 root.insert(copy, first);
611 families
612 .entry(first)
613 .or_insert_with(|| vec![first])
614 .push(copy);
615 }
616 let mut vertex_of: HashMap<u32, u32> = HashMap::default();
617 for (vertex, &particle) in particles.iter().enumerate() {
618 vertex_of.entry(particle).or_insert(vertex as u32);
619 }
620 for element in indices.iter_mut() {
621 let used = element_vertices(element).len();
622 let current: Vec<u32> = element[..used]
623 .iter()
624 .map(|&w| particles[w as usize])
625 .collect();
626 let candidates: Vec<&[u32]> = current
627 .iter()
628 .map(|p| {
629 let first = root.get(p).copied().unwrap_or(*p);
630 families
631 .get(&first)
632 .map_or(core::slice::from_ref(p), Vec::as_slice)
633 })
634 .collect();
635 let combinations: usize = candidates.iter().map(|c| c.len()).product();
636 if combinations == 1
637 || combinations > MAX_SPLIT_COMBINATIONS
638 || body.measure_element_holds(¤t)
639 {
640 continue;
641 }
642 for index in 0..combinations {
643 let mut digits = index;
644 let combination: Vec<u32> = candidates
645 .iter()
646 .map(|c| {
647 let p = c[digits % c.len()];
648 digits /= c.len();
649 p
650 })
651 .collect();
652 if body.measure_element_holds(&combination) {
653 for (k, &p) in combination.iter().enumerate() {
654 if p != current[k] {
655 element[k] = *vertex_of.entry(p).or_insert_with(|| {
656 particles.push(p);
657 particles.len() as u32 - 1
658 });
659 }
660 }
661 break;
662 }
663 }
664 }
665}