use super::{SoftBody, SoftCollisionMesh, SoftMeshRef};
use crate::alloc_prelude::*;
use crate::math::{AngVector, DIM, Pose, Real, Vector};
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub(crate) struct SoftEdgeContact {
pub other: SoftMeshRef,
pub edge: u32,
pub other_edge: u32,
pub impulse: Real,
pub tangent_impulse: Vector,
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub(crate) struct SoftVertexContact {
pub other: SoftMeshRef,
pub vertex: u32,
pub element: u32,
pub flipped: bool,
pub impulse: Real,
pub tangent_impulse: Vector,
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub(crate) struct SoftOverlapState {
pub other: crate::geometry::ColliderHandle,
pub best_error: Real,
pub steps_stuck: u16,
pub stalled: bool,
}
#[derive(Clone, Debug)]
pub(crate) struct SoftOverlapWarm {
pub other: crate::geometry::ColliderHandle,
pub own: Vec<(u32, Vector)>,
pub other_soft: Vec<(u32, Vector)>,
pub rigid: (Vector, AngVector),
}
#[derive(Clone, Debug)]
pub struct SoftVolumeContact {
pub center: Vector,
pub normal: Vector,
pub volume: Real,
pub gradients: Vec<(Vector, Vector)>,
}
impl SoftBody {
fn mesh_vertex(sb: &SoftBody, mesh: &SoftCollisionMesh, i: u32) -> Option<Vector> {
((i as usize) < mesh.vertex_count()).then(|| mesh.vertex(sb, i as usize))
}
fn mesh_edge(sb: &SoftBody, mesh: &SoftCollisionMesh, i: u32) -> Option<parry::shape::Segment> {
#[cfg(feature = "dim2")]
let vertices = *mesh.indices.get(i as usize)?;
#[cfg(feature = "dim3")]
let vertices = *mesh.edges.get(i as usize)?;
Some(parry::shape::Segment::new(
Self::mesh_vertex(sb, mesh, vertices[0])?,
Self::mesh_vertex(sb, mesh, vertices[1])?,
))
}
pub fn edge_contact_segments<'a>(
&'a self,
soft_bodies: &'a super::SoftBodySet,
) -> impl Iterator<Item = (Vector, Vector)> + 'a {
self.meshes()
.flat_map(|mesh| mesh.edge_contacts.iter().map(move |c| (mesh, c)))
.filter_map(move |(mesh, c)| {
let other = soft_bodies.get(c.other.body)?;
let seg1 = Self::mesh_edge(self, mesh, c.edge)?;
let seg2 = Self::mesh_edge(other, other.mesh(c.other.id)?, c.other_edge)?;
let (loc1, loc2) =
parry::query::details::closest_points_segment_segment_with_locations(
&Pose::IDENTITY,
&seg1,
&seg2,
);
Some((seg1.point_at(&loc1), seg2.point_at(&loc2)))
})
}
pub fn volume_contacts(&self) -> impl Iterator<Item = &SoftVolumeContact> + '_ {
self.meshes().flat_map(|mesh| mesh.volume_contacts.iter())
}
pub fn vertex_contact_segments<'a>(
&'a self,
soft_bodies: &'a super::SoftBodySet,
) -> impl Iterator<Item = (Vector, Vector)> + 'a {
use parry::query::PointQuery;
self.meshes()
.flat_map(|mesh| mesh.vertex_contacts.iter().map(move |c| (mesh, c)))
.filter_map(move |(mesh, c)| {
let other = soft_bodies.get(c.other.body)?;
let other_mesh = other.mesh(c.other.id)?;
let ((vertex_body, vertex_mesh), (element_body, element_mesh)) = if c.flipped {
((self, mesh), (other, other_mesh))
} else {
((other, other_mesh), (self, mesh))
};
let point = Self::mesh_vertex(vertex_body, vertex_mesh, c.vertex)?;
let element = *element_mesh.indices.get(c.element as usize)?;
let mut vtx = [Vector::ZERO; DIM];
for (dest, v) in vtx.iter_mut().zip(&element) {
*dest = Self::mesh_vertex(element_body, element_mesh, *v)?;
}
#[cfg(feature = "dim2")]
let projection = parry::shape::Segment::new(vtx[0], vtx[1]);
#[cfg(feature = "dim3")]
let projection = parry::shape::Triangle::new(vtx[0], vtx[1], vtx[2]);
Some((point, projection.project_local_point(point, false).point))
})
}
}
#[cfg(all(test, feature = "dim2"))]
mod test {
use crate::alloc_prelude::*;
use crate::dynamics::soft_body::SoftVertexContact;
use crate::prelude::*;
fn clamped_projection(p: Vector, a: Vector, b: Vector) -> Vector {
let ab = b - a;
let t = ((p - a).dot(ab) / ab.length_squared()).clamp(0.0, 1.0);
a + ab * t
}
#[test]
fn vertex_contact_segment_ends_at_the_witness_point() {
let mut world = PhysicsWorld::new();
let mut positions = Vec::new();
for i in 0..4u32 {
for j in 0..4u32 {
positions.push(Vector::new(i as Real * 1.7, 10.0 + j as Real * 1.3));
}
}
let mut cells = Vec::new();
for i in 0..3u32 {
for j in 0..3u32 {
let (a, b, c, d) = (
i * 4 + j,
i * 4 + j + 1,
(i + 1) * 4 + j,
(i + 1) * 4 + j + 1,
);
cells.push([a, b, c]);
cells.push([b, d, c]);
}
}
let handle = world.insert_soft_body(SoftBodyBuilder::new(positions).cells(cells));
world.step();
let sb = &world.soft_bodies[handle];
let mesh = sb.meshes().next().expect("the body has a collision mesh");
let id = mesh.id();
let mut best = (0u32, 0u32, -1.0 as Real);
for v in 0..mesh.vertex_count() as u32 {
for (e, element) in mesh.indices().iter().enumerate() {
if element.contains(&v) {
continue;
}
let p = mesh.vertex(sb, v as usize);
let (a, b) = (
mesh.vertex(sb, element[0] as usize),
mesh.vertex(sb, element[1] as usize),
);
let spread = (clamped_projection(p, a, b) - (a + b) * 0.5).length();
if spread > best.2 {
best = (v, e as u32, spread);
}
}
}
let (vertex, element, spread) = best;
assert!(spread > 0.5, "no off-center pair in this mesh");
let sb = world.soft_bodies.get_mut(handle).unwrap();
let mesh = sb.mesh_mut(id).unwrap();
mesh.vertex_contacts.clear();
mesh.vertex_contacts.push(SoftVertexContact {
other: super::SoftMeshRef { body: handle, id },
vertex,
element,
flipped: false,
impulse: 0.0,
tangent_impulse: Vector::ZERO,
});
let sb = &world.soft_bodies[handle];
let mesh = sb.meshes().next().unwrap();
let segments: Vec<_> = sb.vertex_contact_segments(&world.soft_bodies).collect();
assert_eq!(segments.len(), 1);
let idx = mesh.indices()[element as usize];
let p = mesh.vertex(sb, vertex as usize);
let (a, b) = (
mesh.vertex(sb, idx[0] as usize),
mesh.vertex(sb, idx[1] as usize),
);
let (drawn_from, drawn_to) = segments[0];
assert!((drawn_from - p).length() < 1.0e-5);
assert!((drawn_to - clamped_projection(p, a, b)).length() < 1.0e-5);
assert!((drawn_to - (a + b) * 0.5).length() > 0.5);
}
}