#[cfg(doc)]
use super::Collider;
use super::CollisionEvent;
use crate::dynamics::{RigidBodyHandle, RigidBodySet};
use crate::geometry::{ColliderHandle, ColliderSet, Contact, ContactManifold};
use crate::math::{Real, TangentImpulse, Vector};
use crate::pipeline::EventHandler;
use crate::prelude::CollisionEventFlags;
use crate::utils::ScalarType;
use parry::math::{SIMD_WIDTH, SimdReal};
use parry::query::ContactManifoldsWorkspace;
bitflags::bitflags! {
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct SolverFlags: u32 {
const COMPUTE_IMPULSES = 0b001;
}
}
impl Default for SolverFlags {
fn default() -> Self {
SolverFlags::COMPUTE_IMPULSES
}
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct ContactData {
pub impulse: Real,
pub tangent_impulse: TangentImpulse<Real>,
pub warmstart_impulse: Real,
pub warmstart_tangent_impulse: TangentImpulse<Real>,
#[cfg(feature = "dim3")]
pub warmstart_twist_impulse: Real,
}
impl Default for ContactData {
fn default() -> Self {
Self {
impulse: 0.0,
tangent_impulse: na::zero(),
warmstart_impulse: 0.0,
warmstart_tangent_impulse: na::zero(),
#[cfg(feature = "dim3")]
warmstart_twist_impulse: 0.0,
}
}
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug)]
pub struct IntersectionPair {
pub intersecting: bool,
pub(crate) start_event_emitted: bool,
}
impl IntersectionPair {
pub(crate) fn new() -> Self {
Self {
intersecting: false,
start_event_emitted: false,
}
}
pub(crate) fn emit_start_event(
&mut self,
bodies: &RigidBodySet,
colliders: &ColliderSet,
collider1: ColliderHandle,
collider2: ColliderHandle,
events: &dyn EventHandler,
) {
self.start_event_emitted = true;
events.handle_collision_event(
bodies,
colliders,
CollisionEvent::Started(collider1, collider2, CollisionEventFlags::SENSOR),
None,
);
}
pub(crate) fn emit_stop_event(
&mut self,
bodies: &RigidBodySet,
colliders: &ColliderSet,
collider1: ColliderHandle,
collider2: ColliderHandle,
events: &dyn EventHandler,
) {
self.start_event_emitted = false;
events.handle_collision_event(
bodies,
colliders,
CollisionEvent::Stopped(collider1, collider2, CollisionEventFlags::SENSOR),
None,
);
}
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Clone)]
pub struct ContactPair {
pub collider1: ColliderHandle,
pub collider2: ColliderHandle,
pub manifolds: Vec<ContactManifold>,
pub(crate) start_event_emitted: bool,
pub(crate) workspace: Option<ContactManifoldsWorkspace>,
}
impl Default for ContactPair {
fn default() -> Self {
Self::new(ColliderHandle::invalid(), ColliderHandle::invalid())
}
}
impl ContactPair {
pub(crate) fn new(collider1: ColliderHandle, collider2: ColliderHandle) -> Self {
Self {
collider1,
collider2,
manifolds: Vec::new(),
start_event_emitted: false,
workspace: None,
}
}
pub fn has_any_active_contact(&self) -> bool {
self.manifolds
.iter()
.any(|m| !m.data.solver_contacts.is_empty())
}
pub fn clear(&mut self) {
self.manifolds.clear();
self.workspace = None;
}
pub fn total_impulse(&self) -> Vector {
self.manifolds
.iter()
.map(|m| m.total_impulse() * m.data.normal)
.sum()
}
pub fn total_impulse_magnitude(&self) -> Real {
self.manifolds
.iter()
.fold(0.0, |a, m| a + m.total_impulse())
}
pub fn max_impulse(&self) -> (Real, Vector) {
let mut result = (0.0, Vector::ZERO);
for m in &self.manifolds {
let impulse = m.total_impulse();
if impulse > result.0 {
result = (impulse, m.data.normal);
}
}
result
}
#[profiling::function]
pub fn find_deepest_contact(&self) -> Option<(&ContactManifold, &Contact)> {
let mut deepest = None;
for m2 in &self.manifolds {
let deepest_candidate = m2.find_deepest_contact();
deepest = match (deepest, deepest_candidate) {
(_, None) => deepest,
(None, Some(c2)) => Some((m2, c2)),
(Some((m1, c1)), Some(c2)) => {
if c1.dist <= c2.dist {
Some((m1, c1))
} else {
Some((m2, c2))
}
}
}
}
deepest
}
pub(crate) fn emit_start_event(
&mut self,
bodies: &RigidBodySet,
colliders: &ColliderSet,
events: &dyn EventHandler,
) {
self.start_event_emitted = true;
events.handle_collision_event(
bodies,
colliders,
CollisionEvent::Started(self.collider1, self.collider2, CollisionEventFlags::empty()),
Some(self),
);
}
pub(crate) fn emit_stop_event(
&mut self,
bodies: &RigidBodySet,
colliders: &ColliderSet,
events: &dyn EventHandler,
) {
self.start_event_emitted = false;
events.handle_collision_event(
bodies,
colliders,
CollisionEvent::Stopped(self.collider1, self.collider2, CollisionEventFlags::empty()),
Some(self),
);
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct ContactManifoldData {
pub rigid_body1: Option<RigidBodyHandle>,
pub rigid_body2: Option<RigidBodyHandle>,
pub solver_flags: SolverFlags,
pub normal: Vector,
pub solver_contacts: Vec<SolverContact>,
pub relative_dominance: i16,
pub user_data: u32,
}
pub type SolverContact = SolverContactGeneric<Real, 1>;
pub type SimdSolverContact = SolverContactGeneric<SimdReal, SIMD_WIDTH>;
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde-serialize",
serde(bound(
serialize = "N: serde::Serialize, N::Vector: serde::Serialize, [u32; LANES]: serde::Serialize"
))
)]
#[cfg_attr(
feature = "serde-serialize",
serde(bound(
deserialize = "N: serde::Deserialize<'de>, N::Vector: serde::Deserialize<'de>, [u32; LANES]: serde::Deserialize<'de>"
))
)]
#[repr(C)]
#[repr(align(16))]
pub struct SolverContactGeneric<N: ScalarType, const LANES: usize> {
pub point: N::Vector, pub dist: N, pub friction: N, pub restitution: N, pub tangent_velocity: N::Vector, pub warmstart_impulse: N, pub warmstart_tangent_impulse: TangentImpulse<N>, pub warmstart_twist_impulse: N, pub is_new: N, pub contact_id: [u32; LANES], #[cfg(feature = "dim3")]
pub(crate) padding: [N; 1],
}
#[repr(C)]
#[repr(align(16))]
pub struct SimdSolverContactRepr {
data0: SimdReal,
data1: SimdReal,
data2: SimdReal,
#[cfg(feature = "dim3")]
data3: SimdReal,
}
static_assertions::const_assert_eq!(
align_of::<SimdSolverContactRepr>(),
align_of::<SolverContact>()
);
#[cfg(feature = "simd-is-enabled")]
static_assertions::assert_eq_size!(SimdSolverContactRepr, SolverContact);
static_assertions::const_assert_eq!(
align_of::<SimdSolverContact>(),
align_of::<[SolverContact; SIMD_WIDTH]>()
);
#[cfg(feature = "simd-is-enabled")]
static_assertions::assert_eq_size!(SimdSolverContact, [SolverContact; SIMD_WIDTH]);
impl SimdSolverContact {
#[cfg(not(feature = "simd-is-enabled"))]
pub unsafe fn gather_unchecked(contacts: &[&[SolverContact]; SIMD_WIDTH], k: usize) -> Self {
contacts[0][k]
}
#[cfg(feature = "simd-is-enabled")]
pub unsafe fn gather_unchecked(contacts: &[&[SolverContact]; SIMD_WIDTH], k: usize) -> Self {
let data_repr: &[&[SimdSolverContactRepr]; SIMD_WIDTH] =
unsafe { std::mem::transmute(contacts) };
let aos0 = [
unsafe { data_repr[0].get_unchecked(k).data0.0 },
unsafe { data_repr[1].get_unchecked(k).data0.0 },
unsafe { data_repr[2].get_unchecked(k).data0.0 },
unsafe { data_repr[3].get_unchecked(k).data0.0 },
];
let aos1 = [
unsafe { data_repr[0].get_unchecked(k).data1.0 },
unsafe { data_repr[1].get_unchecked(k).data1.0 },
unsafe { data_repr[2].get_unchecked(k).data1.0 },
unsafe { data_repr[3].get_unchecked(k).data1.0 },
];
let aos2 = [
unsafe { data_repr[0].get_unchecked(k).data2.0 },
unsafe { data_repr[1].get_unchecked(k).data2.0 },
unsafe { data_repr[2].get_unchecked(k).data2.0 },
unsafe { data_repr[3].get_unchecked(k).data2.0 },
];
#[cfg(feature = "dim3")]
let aos3 = [
unsafe { data_repr[0].get_unchecked(k).data3.0 },
unsafe { data_repr[1].get_unchecked(k).data3.0 },
unsafe { data_repr[2].get_unchecked(k).data3.0 },
unsafe { data_repr[3].get_unchecked(k).data3.0 },
];
use crate::utils::transmute_to_wide;
let soa0 = wide::f32x4::transpose(transmute_to_wide(aos0));
let soa1 = wide::f32x4::transpose(transmute_to_wide(aos1));
let soa2 = wide::f32x4::transpose(transmute_to_wide(aos2));
#[cfg(feature = "dim3")]
let soa3 = wide::f32x4::transpose(transmute_to_wide(aos3));
#[cfg(feature = "dim2")]
return unsafe {
std::mem::transmute::<[[wide::f32x4; 4]; 3], SolverContactGeneric<SimdReal, 4>>([
soa0, soa1, soa2,
])
};
#[cfg(feature = "dim3")]
return unsafe {
std::mem::transmute::<[[wide::f32x4; 4]; 4], SolverContactGeneric<SimdReal, 4>>([
soa0, soa1, soa2, soa3,
])
};
}
}
#[cfg(feature = "simd-is-enabled")]
impl SimdSolverContact {
pub fn is_bouncy(&self) -> SimdReal {
use na::{SimdPartialOrd, SimdValue};
let one = SimdReal::splat(1.0);
let zero = SimdReal::splat(0.0);
let if_new = one.select(self.restitution.simd_gt(zero), zero);
let if_not_new = one.select(self.restitution.simd_ge(one), zero);
if_new.select(self.is_new.simd_ne(zero), if_not_new)
}
}
impl SolverContact {
pub fn is_bouncy(&self) -> Real {
if self.is_new != 0.0 {
(self.restitution > 0.0) as u32 as Real
} else {
(self.restitution >= 1.0) as u32 as Real
}
}
}
impl Default for ContactManifoldData {
fn default() -> Self {
Self::new(None, None, SolverFlags::empty())
}
}
impl ContactManifoldData {
pub(crate) fn new(
rigid_body1: Option<RigidBodyHandle>,
rigid_body2: Option<RigidBodyHandle>,
solver_flags: SolverFlags,
) -> ContactManifoldData {
Self {
rigid_body1,
rigid_body2,
solver_flags,
normal: Vector::ZERO,
solver_contacts: Vec::new(),
relative_dominance: 0,
user_data: 0,
}
}
#[inline]
pub fn num_active_contacts(&self) -> usize {
self.solver_contacts.len()
}
}
pub trait ContactManifoldExt {
fn total_impulse(&self) -> Real;
}
impl ContactManifoldExt for ContactManifold {
fn total_impulse(&self) -> Real {
self.points.iter().map(|pt| pt.data.impulse).sum()
}
}