use super::CollisionEvent;
use crate::alloc_prelude::*;
use crate::dynamics::{RigidBodyHandle, RigidBodySet};
use crate::geometry::{ColliderHandle, ColliderSet, Contact, ContactManifold};
use crate::math::{Pose, Real, TangentImpulse, Vector};
use crate::pipeline::EventHandler;
use crate::prelude::CollisionEventFlags;
use crate::utils::ScalarType;
use crate::utils::SolverBlock;
use parry::math::{SIMD_WIDTH, SimdReal};
use parry::query::ContactManifoldsWorkspace;
#[cfg(not(feature = "std"))]
use simba::scalar::ComplexField as _;
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,
#[cfg(feature = "dim3")]
#[cfg_attr(feature = "serde-serialize", serde(default))]
pub warmstart_tangent_world: Vector,
#[cfg_attr(feature = "serde-serialize", serde(default))]
pub solver_dp1: Vector,
#[cfg_attr(feature = "serde-serialize", serde(default))]
pub solver_dp2: Vector,
}
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(feature = "dim3")]
warmstart_tangent_world: Vector::ZERO,
solver_dp1: Vector::ZERO,
solver_dp2: Vector::ZERO,
}
}
}
#[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,
);
}
}
pub(crate) const SOLVER_COLOR_UNCOLORED: u8 = u8::MAX;
pub(crate) const SOLVER_COLOR_OVERFLOW: u8 = 128;
pub(crate) const SOLVER_DYNAMIC_COLOR_COUNT: u32 = 120;
#[cfg(feature = "serde-serialize")]
fn default_solver_color() -> u8 {
SOLVER_COLOR_UNCOLORED
}
#[cfg(feature = "serde-serialize")]
fn default_solver_color_bodies() -> [u32; 2] {
[u32::MAX; 2]
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Clone)]
pub struct ContactPair {
pub collider1: ColliderHandle,
pub collider2: ColliderHandle,
pub manifolds: Vec<ContactManifold>,
pub solver_clusters: Vec<ContactManifold>,
#[cfg_attr(feature = "serde-serialize", serde(skip))]
pub(crate) solver_clusters_prev: Vec<ContactManifold>,
#[cfg_attr(feature = "serde-serialize", serde(default = "default_solver_color"))]
pub(crate) solver_color: u8,
#[cfg_attr(
feature = "serde-serialize",
serde(default = "default_solver_color_bodies")
)]
pub(crate) solver_color_bodies: [u32; 2],
pub(crate) start_event_emitted: bool,
pub(crate) workspace: Option<ContactManifoldsWorkspace>,
pub(crate) recycle_state: Option<ContactRecycleState>,
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub(crate) struct ContactRecycleState {
pub pos12: Pose,
pub rot1: crate::math::Rotation,
pub rot2: crate::math::Rotation,
pub max_extent: Real,
pub max_drift: Real,
}
#[inline]
pub(crate) fn relative_rot_cos(base: &crate::math::Rotation, cur: &crate::math::Rotation) -> Real {
#[cfg(feature = "dim2")]
{
base.dot(*cur)
}
#[cfg(feature = "dim3")]
{
let c = base.dot(*cur);
2.0 * c * c - 1.0
}
}
#[inline]
pub(crate) fn relative_pose_drift(base: &Pose, cur: &Pose, max_extent: Real) -> Real {
let trans = (cur.translation - base.translation).length();
#[cfg(feature = "dim2")]
let rot_chord = {
let c = base.rotation.dot(cur.rotation);
(2.0 * (1.0 - c)).max(0.0).sqrt() * max_extent
};
#[cfg(feature = "dim3")]
let rot_chord = {
let c = base.rotation.dot(cur.rotation);
2.0 * (1.0 - c * c).max(0.0).sqrt() * max_extent
};
trans + rot_chord
}
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(),
solver_clusters: Vec::new(),
solver_clusters_prev: Vec::new(),
solver_color: SOLVER_COLOR_UNCOLORED,
solver_color_bodies: [u32::MAX; 2],
start_event_emitted: false,
workspace: None,
recycle_state: None,
}
}
pub(crate) fn reset_for_reuse(&mut self, collider1: ColliderHandle, collider2: ColliderHandle) {
self.collider1 = collider1;
self.collider2 = collider2;
self.manifolds.clear();
self.solver_clusters.clear();
self.solver_clusters_prev.clear();
self.solver_color = SOLVER_COLOR_UNCOLORED;
self.solver_color_bodies = [u32::MAX; 2];
self.start_event_emitted = false;
self.workspace = None;
self.recycle_state = None;
}
pub fn solver_manifolds(&self) -> &[ContactManifold] {
if self.solver_clusters.is_empty() {
&self.manifolds
} else {
&self.solver_clusters
}
}
#[cfg_attr(feature = "parallel", allow(dead_code))] pub(crate) fn solver_manifolds_mut(&mut self) -> &mut [ContactManifold] {
if self.solver_clusters.is_empty() {
&mut self.manifolds
} else {
&mut self.solver_clusters
}
}
pub fn has_any_active_contact(&self) -> bool {
self.solver_manifolds()
.iter()
.any(|m| !m.data.solver_contacts.is_empty())
}
pub fn clear(&mut self) {
self.manifolds.clear();
self.solver_clusters.clear();
self.solver_clusters_prev.clear();
self.workspace = None;
self.recycle_state = None;
}
pub fn total_impulse(&self) -> Vector {
self.solver_manifolds()
.iter()
.map(|m| m.total_impulse() * m.data.normal)
.sum()
}
pub fn total_impulse_magnitude(&self) -> Real {
self.solver_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.solver_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,
#[cfg_attr(feature = "serde-serialize", serde(default = "default_solver_color"))]
pub(crate) solver_color: u8,
pub(crate) solver_body_ids: [u32; 2],
#[cfg_attr(feature = "parallel", allow(dead_code))] pub(crate) graph_pos: crate::dynamics::solver::solver_contact_graph::GraphPos,
pub normal: Vector,
pub solver_contacts: SolverContacts,
pub relative_dominance: i16,
pub user_data: u32,
#[cfg_attr(feature = "serde-serialize", serde(default))]
pub friction: Real,
#[cfg_attr(feature = "serde-serialize", serde(default))]
pub restitution: Real,
}
pub type SolverContact = SolverContactGeneric<Real, 1>;
#[cfg(feature = "dim2")]
pub type SolverContacts = arrayvec::ArrayVec<SolverContact, 2>;
#[cfg(feature = "dim3")]
pub type SolverContacts = Vec<SolverContact>;
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, [ContactId; LANES]: serde::Serialize"
))
)]
#[cfg_attr(
feature = "serde-serialize",
serde(bound(
deserialize = "N: serde::Deserialize<'de>, N::Vector: serde::Deserialize<'de>, [ContactId; LANES]: serde::Deserialize<'de>"
))
)]
#[repr(C)]
#[repr(align(16))]
pub struct SolverContactGeneric<N: ScalarType, const LANES: usize> {
pub anchor1: N::Vector, pub anchor2: N::Vector, pub dist: N, pub tangent_velocity: N::Vector, pub contact_id: [ContactId; LANES], #[cfg(feature = "dim3")]
pub(crate) padding: [N; 1],
}
#[cfg(feature = "f32")]
pub type ContactId = u32;
#[cfg(feature = "f64")]
pub type ContactId = u64;
pub const NEW_CONTACT_BIT: ContactId = 1 << 31;
#[repr(C)]
#[repr(align(16))]
pub struct SimdSolverContactRepr {
data0: SolverBlock,
data1: SolverBlock,
#[cfg(feature = "dim3")]
data2: SolverBlock,
}
static_assertions::const_assert_eq!(
align_of::<SimdSolverContactRepr>(),
align_of::<SolverContact>()
);
static_assertions::assert_eq_size!(SimdSolverContactRepr, SolverContact);
static_assertions::const_assert_eq!(
align_of::<SimdSolverContact>() % align_of::<[SolverContact; SIMD_WIDTH]>(),
0
);
static_assertions::assert_eq_size!(SimdSolverContact, [SolverContact; SIMD_WIDTH]);
impl SimdSolverContact {
pub unsafe fn gather_unchecked(
contacts: &[&[SolverContact]; SIMD_WIDTH],
ks: [usize; SIMD_WIDTH],
) -> Self {
let data_repr: &[&[SimdSolverContactRepr]; SIMD_WIDTH] =
unsafe { core::mem::transmute(contacts) };
use crate::utils::transpose_wide;
let aos0: [_; SIMD_WIDTH] =
core::array::from_fn(|k| unsafe { data_repr[k].get_unchecked(ks[k]).data0.0 });
let aos1: [_; SIMD_WIDTH] =
core::array::from_fn(|k| unsafe { data_repr[k].get_unchecked(ks[k]).data1.0 });
let soa0 = transpose_wide(aos0);
let soa1 = transpose_wide(aos1);
#[cfg(feature = "dim2")]
unsafe {
core::mem::transmute::<[[SimdReal; 4]; 2], SimdSolverContact>([soa0, soa1])
}
#[cfg(feature = "dim3")]
{
let aos2: [_; SIMD_WIDTH] =
core::array::from_fn(|k| unsafe { data_repr[k].get_unchecked(ks[k]).data2.0 });
let soa2 = transpose_wide(aos2);
unsafe {
core::mem::transmute::<[[SimdReal; 4]; 3], SimdSolverContact>([soa0, soa1, soa2])
}
}
}
}
impl<N: ScalarType, const LANES: usize> SolverContactGeneric<N, LANES> {
#[inline]
pub fn contact_indices(&self) -> [ContactId; LANES] {
self.contact_id.map(|id| id & !NEW_CONTACT_BIT)
}
}
pub fn is_bouncy_simd(restitution: SimdReal, is_new: SimdReal) -> SimdReal {
use na::{SimdPartialOrd, SimdValue};
let one = SimdReal::splat(1.0);
let zero = SimdReal::splat(0.0);
let if_new = one.select(restitution.simd_gt(zero), zero);
let if_not_new = one.select(restitution.simd_ge(one), zero);
if_new.select(is_new.simd_ne(zero), if_not_new)
}
pub fn is_bouncy(restitution: Real, is_new: bool) -> Real {
if is_new {
(restitution > 0.0) as u32 as Real
} else {
(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,
solver_color: SOLVER_COLOR_UNCOLORED,
solver_body_ids: [u32::MAX; 2],
graph_pos: crate::dynamics::solver::solver_contact_graph::GraphPos::NONE,
normal: Vector::ZERO,
solver_contacts: SolverContacts::new(),
relative_dominance: 0,
user_data: 0,
friction: 0.0,
restitution: 0.0,
}
}
pub fn solver_contact_world_points(
&self,
contact: &SolverContact,
bodies: &crate::dynamics::RigidBodySet,
) -> (Vector, Vector) {
let resolve =
|anchor: Vector, handle: Option<RigidBodyHandle>, world_attached: bool| match handle
.filter(|_| !world_attached)
.and_then(|h| bodies.get(h))
{
Some(rb) => rb.pos.position * (rb.mprops.local_mprops.local_com + anchor),
None => anchor,
};
(
resolve(
contact.anchor1,
self.rigid_body1,
self.relative_dominance > 0,
),
resolve(
contact.anchor2,
self.rigid_body2,
self.relative_dominance < 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()
}
}