rapier3d/dynamics/soft_body/
soft_body_contacts.rs1use super::{SoftBody, SoftCollisionMesh, SoftMeshRef};
3use crate::alloc_prelude::*;
4use crate::math::{AngVector, DIM, Pose, Real, Vector};
5
6#[derive(Copy, Clone, Debug)]
9#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
10pub(crate) struct SoftEdgeContact {
11 pub other: SoftMeshRef,
13 pub edge: u32,
15 pub other_edge: u32,
16 pub impulse: Real,
18 pub tangent_impulse: Vector,
19}
20
21#[derive(Copy, Clone, Debug)]
25#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
26pub(crate) struct SoftVertexContact {
27 pub other: SoftMeshRef,
29 pub vertex: u32,
33 pub element: u32,
34 pub flipped: bool,
35 pub impulse: Real,
37 pub tangent_impulse: Vector,
38}
39
40#[derive(Copy, Clone, Debug)]
44#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
45pub(crate) struct SoftOverlapState {
46 pub other: crate::geometry::ColliderHandle,
48 pub best_error: Real,
50 pub steps_stuck: u16,
52 pub stalled: bool,
55}
56
57#[derive(Clone, Debug)]
61pub(crate) struct SoftOverlapWarm {
62 pub other: crate::geometry::ColliderHandle,
63 pub own: Vec<(u32, Vector)>,
65 pub other_soft: Vec<(u32, Vector)>,
67 pub rigid: (Vector, AngVector),
69}
70
71#[derive(Clone, Debug)]
75pub struct SoftVolumeContact {
76 pub center: Vector,
78 pub normal: Vector,
80 pub volume: Real,
82 pub gradients: Vec<(Vector, Vector)>,
84}
85
86impl SoftBody {
87 fn mesh_vertex(sb: &SoftBody, mesh: &SoftCollisionMesh, i: u32) -> Option<Vector> {
90 ((i as usize) < mesh.vertex_count()).then(|| mesh.vertex(sb, i as usize))
91 }
92
93 fn mesh_edge(sb: &SoftBody, mesh: &SoftCollisionMesh, i: u32) -> Option<parry::shape::Segment> {
95 #[cfg(feature = "dim2")]
96 let vertices = *mesh.indices.get(i as usize)?;
97 #[cfg(feature = "dim3")]
98 let vertices = *mesh.edges.get(i as usize)?;
99 Some(parry::shape::Segment::new(
100 Self::mesh_vertex(sb, mesh, vertices[0])?,
101 Self::mesh_vertex(sb, mesh, vertices[1])?,
102 ))
103 }
104
105 pub fn edge_contact_segments<'a>(
109 &'a self,
110 soft_bodies: &'a super::SoftBodySet,
111 ) -> impl Iterator<Item = (Vector, Vector)> + 'a {
112 self.meshes()
113 .flat_map(|mesh| mesh.edge_contacts.iter().map(move |c| (mesh, c)))
114 .filter_map(move |(mesh, c)| {
115 let other = soft_bodies.get(c.other.body)?;
117 let seg1 = Self::mesh_edge(self, mesh, c.edge)?;
118 let seg2 = Self::mesh_edge(other, other.mesh(c.other.id)?, c.other_edge)?;
119 let (loc1, loc2) =
120 parry::query::details::closest_points_segment_segment_with_locations(
121 &Pose::IDENTITY,
122 &seg1,
123 &seg2,
124 );
125 Some((seg1.point_at(&loc1), seg2.point_at(&loc2)))
126 })
127 }
128
129 pub fn volume_contacts(&self) -> impl Iterator<Item = &SoftVolumeContact> + '_ {
132 self.meshes().flat_map(|mesh| mesh.volume_contacts.iter())
133 }
134
135 pub fn vertex_contact_segments<'a>(
139 &'a self,
140 soft_bodies: &'a super::SoftBodySet,
141 ) -> impl Iterator<Item = (Vector, Vector)> + 'a {
142 use parry::query::PointQuery;
143
144 self.meshes()
145 .flat_map(|mesh| mesh.vertex_contacts.iter().map(move |c| (mesh, c)))
146 .filter_map(move |(mesh, c)| {
147 let other = soft_bodies.get(c.other.body)?;
148 let other_mesh = other.mesh(c.other.id)?;
149 let ((vertex_body, vertex_mesh), (element_body, element_mesh)) = if c.flipped {
152 ((self, mesh), (other, other_mesh))
153 } else {
154 ((other, other_mesh), (self, mesh))
155 };
156 let point = Self::mesh_vertex(vertex_body, vertex_mesh, c.vertex)?;
157 let element = *element_mesh.indices.get(c.element as usize)?;
158 let mut vtx = [Vector::ZERO; DIM];
159 for (dest, v) in vtx.iter_mut().zip(&element) {
160 *dest = Self::mesh_vertex(element_body, element_mesh, *v)?;
161 }
162 #[cfg(feature = "dim2")]
163 let projection = parry::shape::Segment::new(vtx[0], vtx[1]);
164 #[cfg(feature = "dim3")]
165 let projection = parry::shape::Triangle::new(vtx[0], vtx[1], vtx[2]);
166 Some((point, projection.project_local_point(point, false).point))
167 })
168 }
169}
170
171#[cfg(all(test, feature = "dim2"))]
172mod test {
173 use crate::alloc_prelude::*;
174 use crate::dynamics::soft_body::SoftVertexContact;
175 use crate::prelude::*;
176
177 fn clamped_projection(p: Vector, a: Vector, b: Vector) -> Vector {
179 let ab = b - a;
180 let t = ((p - a).dot(ab) / ab.length_squared()).clamp(0.0, 1.0);
181 a + ab * t
182 }
183
184 #[test]
188 fn vertex_contact_segment_ends_at_the_witness_point() {
189 let mut world = PhysicsWorld::new();
190 let mut positions = Vec::new();
191 for i in 0..4u32 {
192 for j in 0..4u32 {
193 positions.push(Vector::new(i as Real * 1.7, 10.0 + j as Real * 1.3));
194 }
195 }
196 let mut cells = Vec::new();
197 for i in 0..3u32 {
198 for j in 0..3u32 {
199 let (a, b, c, d) = (
200 i * 4 + j,
201 i * 4 + j + 1,
202 (i + 1) * 4 + j,
203 (i + 1) * 4 + j + 1,
204 );
205 cells.push([a, b, c]);
206 cells.push([b, d, c]);
207 }
208 }
209 let handle = world.insert_soft_body(SoftBodyBuilder::new(positions).cells(cells));
210 world.step();
211
212 let sb = &world.soft_bodies[handle];
215 let mesh = sb.meshes().next().expect("the body has a collision mesh");
216 let id = mesh.id();
217 let mut best = (0u32, 0u32, -1.0 as Real);
218 for v in 0..mesh.vertex_count() as u32 {
219 for (e, element) in mesh.indices().iter().enumerate() {
220 if element.contains(&v) {
221 continue;
222 }
223 let p = mesh.vertex(sb, v as usize);
224 let (a, b) = (
225 mesh.vertex(sb, element[0] as usize),
226 mesh.vertex(sb, element[1] as usize),
227 );
228 let spread = (clamped_projection(p, a, b) - (a + b) * 0.5).length();
229 if spread > best.2 {
230 best = (v, e as u32, spread);
231 }
232 }
233 }
234 let (vertex, element, spread) = best;
235 assert!(spread > 0.5, "no off-center pair in this mesh");
236
237 let sb = world.soft_bodies.get_mut(handle).unwrap();
239 let mesh = sb.mesh_mut(id).unwrap();
240 mesh.vertex_contacts.clear();
241 mesh.vertex_contacts.push(SoftVertexContact {
242 other: super::SoftMeshRef { body: handle, id },
243 vertex,
244 element,
245 flipped: false,
246 impulse: 0.0,
247 tangent_impulse: Vector::ZERO,
248 });
249
250 let sb = &world.soft_bodies[handle];
251 let mesh = sb.meshes().next().unwrap();
252 let segments: Vec<_> = sb.vertex_contact_segments(&world.soft_bodies).collect();
253 assert_eq!(segments.len(), 1);
254
255 let idx = mesh.indices()[element as usize];
256 let p = mesh.vertex(sb, vertex as usize);
257 let (a, b) = (
258 mesh.vertex(sb, idx[0] as usize),
259 mesh.vertex(sb, idx[1] as usize),
260 );
261 let (drawn_from, drawn_to) = segments[0];
262 assert!((drawn_from - p).length() < 1.0e-5);
263 assert!((drawn_to - clamped_projection(p, a, b)).length() < 1.0e-5);
264 assert!((drawn_to - (a + b) * 0.5).length() > 0.5);
266 }
267}