use crate::geometry::ColliderHandle;
use crate::math::{DIM, Real, Vector};
use super::{SoftBody, SoftCollisionMesh, SoftMeshCellBinding, SoftMeshId, SoftMeshMapping};
impl SoftCollisionMesh {
pub fn id(&self) -> SoftMeshId {
self.id
}
pub(crate) fn set_id(&mut self, id: SoftMeshId) {
self.id = id;
}
pub fn collider(&self) -> ColliderHandle {
self.collider
}
pub fn collision_enabled(&self) -> bool {
self.collision_enabled
}
pub fn indices(&self) -> &[[u32; DIM]] {
&self.indices
}
#[inline]
pub fn element(&self, i: usize) -> &[u32] {
super::soft_body_builder::element_vertices(&self.indices[i])
}
#[inline]
pub fn arity(&self) -> usize {
self.indices
.first()
.map_or(DIM, |e| super::soft_body_builder::element_vertices(e).len())
}
#[inline]
pub fn is_wire(&self) -> bool {
self.arity() < DIM
}
pub(crate) fn element_edge_ids(&self, i: usize) -> impl Iterator<Item = u32> + '_ {
#[cfg(feature = "dim3")]
let ids: &[u32] = if self.is_wire() {
&[]
} else {
super::soft_body_builder::element_vertices(&self.element_edges[i])
};
#[cfg(feature = "dim2")]
let ids: &[u32] = &[];
let own = ids.is_empty().then_some(i as u32);
own.into_iter().chain(ids.iter().copied())
}
pub(crate) fn edge_vertices(&self, edge: u32) -> [u32; 2] {
#[cfg(feature = "dim3")]
if !self.is_wire() {
return self.edges[edge as usize];
}
let element = self.element(edge as usize);
[element[0], element[1]]
}
pub(crate) fn edge_owner(&self, edge: u32) -> u32 {
#[cfg(feature = "dim3")]
if !self.is_wire() {
return self.edge_owners[edge as usize];
}
edge
}
pub fn binding(&self) -> &SoftMeshMapping {
&self.binding
}
pub fn is_skinned(&self) -> bool {
matches!(self.binding, SoftMeshMapping::Skinned { .. })
}
pub fn is_closed(&self) -> bool {
self.closed
}
pub fn is_oriented(&self) -> bool {
self.oriented
}
pub fn is_solid(&self) -> bool {
self.closed && self.oriented
}
pub(crate) fn read_orientation(&mut self, colliders: &crate::geometry::ColliderSet) {
let Some(co) = colliders.get(self.collider) else {
return;
};
#[cfg(feature = "dim2")]
let oriented = co.shape().as_polyline().is_some_and(|polyline| {
polyline
.flags()
.contains(parry::shape::PolylineFlags::ORIENTED)
});
#[cfg(feature = "dim3")]
let oriented = co.shape().as_trimesh().is_some_and(|trimesh| {
trimesh
.flags()
.contains(parry::shape::TriMeshFlags::ORIENTED)
});
self.oriented = oriented;
}
pub fn self_contacts_enabled(&self) -> bool {
self.self_contacts
}
pub fn vertex_count(&self) -> usize {
match &self.binding {
SoftMeshMapping::Direct { particles } => particles.len(),
SoftMeshMapping::Skinned { .. } => self.vertices.len(),
}
}
pub fn vertices(&self) -> &[Vector] {
&self.vertices
}
pub fn vertex_positions<'a>(
&'a self,
body: &'a SoftBody,
) -> impl ExactSizeIterator<Item = Vector> + 'a {
(0..self.vertex_count()).map(move |i| self.vertex(body, i))
}
pub fn vertex(&self, body: &SoftBody, i: usize) -> Vector {
match &self.binding {
SoftMeshMapping::Direct { particles } => body.particles[particles[i] as usize].position,
SoftMeshMapping::Skinned { .. } => self.vertices[i],
}
}
#[inline]
pub(crate) fn cached_vertex(&self, i: usize) -> Vector {
self.vertex_cache[i]
}
pub(crate) fn refresh_vertex_cache(&mut self, particles: &[super::super::SoftBodyParticle]) {
match &self.binding {
SoftMeshMapping::Direct { particles: map } => {
self.vertex_cache.clear();
self.vertex_cache
.extend(map.iter().map(|&p| particles[p as usize].position));
}
SoftMeshMapping::Skinned { .. } => {
self.vertex_cache.clear();
self.vertex_cache.extend_from_slice(&self.vertices);
}
}
}
pub fn vertex_velocity(&self, body: &SoftBody, i: usize) -> Vector {
match &self.binding {
SoftMeshMapping::Direct { particles } => body.particles[particles[i] as usize].velocity,
SoftMeshMapping::Skinned { bindings } => {
let binding = &bindings[i];
let Some(cell) = body.cells.get(binding.cell as usize) else {
return Vector::ZERO;
};
let mut velocity = Vector::ZERO;
for (vid, weight) in cell.vertices.iter().zip(&binding.weights) {
velocity += body.particles[*vid as usize].velocity * *weight;
}
velocity
}
}
}
pub(crate) fn vertex_on_surface(&self, i: usize) -> bool {
match (
self.vertex_elements_offsets.get(i),
self.vertex_elements_offsets.get(i + 1),
) {
(Some(start), Some(end)) => end > start,
_ => false,
}
}
pub fn vertex_anchors(&self, body: &SoftBody, i: usize) -> ([u32; DIM + 1], [Real; DIM + 1]) {
match &self.binding {
SoftMeshMapping::Direct { particles } => {
let mut ids = [u32::MAX; DIM + 1];
let mut weights = [0.0; DIM + 1];
ids[0] = particles[i];
weights[0] = 1.0;
(ids, weights)
}
SoftMeshMapping::Skinned { bindings } => {
match body.cells.get(bindings[i].cell as usize) {
Some(cell) => (cell.vertices, bindings[i].weights),
None => ([u32::MAX; DIM + 1], [0.0; DIM + 1]),
}
}
}
}
pub(crate) fn contact_anchors(
&self,
body: &SoftBody,
vertices: &[u32],
weights: &[Real],
point: Vector,
) -> ([u32; DIM + 1], [Real; DIM + 1], Vector) {
let pad = |vertices: &[u32]| {
let mut ids = [u32::MAX; DIM + 1];
let mut ws = [0.0; DIM + 1];
let len = vertices.len().min(DIM + 1);
ids[..len].copy_from_slice(&vertices[..len]);
ws[..len].copy_from_slice(&weights[..len]);
(ids, ws, point)
};
match &self.binding {
SoftMeshMapping::Direct { particles } => {
let mut ids = [u32::MAX; DIM + 1];
let mut ws = [0.0; DIM + 1];
for k in 0..vertices.len().min(DIM + 1) {
ids[k] = particles[vertices[k] as usize];
ws[k] = weights[k];
}
(ids, ws, point)
}
SoftMeshMapping::Skinned { bindings } => self
.skinned_contact_anchors(body, bindings, vertices, weights, point)
.unwrap_or_else(|| pad(vertices)),
}
}
fn skinned_contact_anchors(
&self,
body: &SoftBody,
bindings: &[SoftMeshCellBinding],
vertices: &[u32],
weights: &[Real],
point: Vector,
) -> Option<([u32; DIM + 1], [Real; DIM + 1], Vector)> {
let first = bindings.get(*vertices.first()? as usize)?;
let shared = vertices
.iter()
.all(|v| bindings[*v as usize].cell == first.cell);
if shared {
let cell = body.cells.get(first.cell as usize)?;
let mut blended = [0.0; DIM + 1];
for (v, weight) in vertices.iter().zip(weights) {
for (w, bw) in blended.iter_mut().zip(&bindings[*v as usize].weights) {
*w += bw * weight;
}
}
return Some((cell.vertices, blended, point));
}
let heaviest = (0..vertices.len()).max_by(|a, b| weights[*a].total_cmp(&weights[*b]))?;
let vertex = vertices[heaviest] as usize;
let binding = &bindings[vertex];
let cell = body.cells.get(binding.cell as usize)?;
Some((cell.vertices, binding.weights, self.vertices[vertex]))
}
pub(crate) fn rest_vertex(&self, body: &SoftBody, i: usize) -> Vector {
match &self.binding {
SoftMeshMapping::Direct { particles } => {
body.particles[particles[i] as usize].rest_position
}
SoftMeshMapping::Skinned { bindings } => {
let binding = &bindings[i];
let Some(cell) = body.cells.get(binding.cell as usize) else {
return Vector::ZERO;
};
let mut position = Vector::ZERO;
for (vid, weight) in cell.vertices.iter().zip(&binding.weights) {
position += body.particles[*vid as usize].rest_position * *weight;
}
position
}
}
}
pub(crate) fn element_cell_ids<'a>(&'a self, body: &'a SoftBody) -> &'a [u32] {
if self.follows_boundary {
&body.boundary_element_cells
} else {
&self.element_cells
}
}
pub(crate) fn vertex_element_ids(&self, vertex: u32) -> &[u32] {
match (
self.vertex_elements_offsets.get(vertex as usize),
self.vertex_elements_offsets.get(vertex as usize + 1),
) {
(Some(&start), Some(&end)) => &self.vertex_elements[start as usize..end as usize],
_ => &[],
}
}
}