367 lines
12 KiB
Rust
367 lines
12 KiB
Rust
use crate::dynamics::{RigidBodyHandle, RigidBodySet};
|
|
use crate::geometry::{ColliderHandle, ColliderSet, Contact, ContactManifold};
|
|
use crate::math::{Point, Real, Vector};
|
|
use crate::pipeline::EventHandler;
|
|
use crate::prelude::CollisionEventFlags;
|
|
use parry::query::ContactManifoldsWorkspace;
|
|
|
|
use super::CollisionEvent;
|
|
|
|
bitflags::bitflags! {
|
|
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
|
/// Flags affecting the behavior of the constraints solver for a given contact manifold.
|
|
pub struct SolverFlags: u32 {
|
|
/// The constraint solver will take this contact manifold into
|
|
/// account for force computation.
|
|
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))]
|
|
/// A single contact between two collider.
|
|
pub struct ContactData {
|
|
/// The impulse, along the contact normal, applied by this contact to the first collider's rigid-body.
|
|
///
|
|
/// The impulse applied to the second collider's rigid-body is given by `-impulse`.
|
|
pub impulse: Real,
|
|
/// The friction impulse along the vector orthonormal to the contact normal, applied to the first
|
|
/// collider's rigid-body.
|
|
#[cfg(feature = "dim2")]
|
|
pub tangent_impulse: Real,
|
|
/// The friction impulses along the basis orthonormal to the contact normal, applied to the first
|
|
/// collider's rigid-body.
|
|
#[cfg(feature = "dim3")]
|
|
pub tangent_impulse: na::Vector2<Real>,
|
|
}
|
|
|
|
impl Default for ContactData {
|
|
fn default() -> Self {
|
|
Self {
|
|
impulse: 0.0,
|
|
tangent_impulse: na::zero(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
|
#[derive(Copy, Clone, Debug)]
|
|
/// The description of all the contacts between a pair of colliders.
|
|
pub struct IntersectionPair {
|
|
/// Are the colliders intersecting?
|
|
pub intersecting: bool,
|
|
/// Was a `CollisionEvent::Started` emitted for this collider?
|
|
pub(crate) start_event_emited: bool,
|
|
}
|
|
|
|
impl IntersectionPair {
|
|
pub(crate) fn new() -> Self {
|
|
Self {
|
|
intersecting: false,
|
|
start_event_emited: false,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn emit_start_event(
|
|
&mut self,
|
|
bodies: &RigidBodySet,
|
|
colliders: &ColliderSet,
|
|
collider1: ColliderHandle,
|
|
collider2: ColliderHandle,
|
|
events: &dyn EventHandler,
|
|
) {
|
|
self.start_event_emited = 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_emited = false;
|
|
events.handle_collision_event(
|
|
bodies,
|
|
colliders,
|
|
CollisionEvent::Stopped(collider1, collider2, CollisionEventFlags::SENSOR),
|
|
None,
|
|
);
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
|
#[derive(Clone)]
|
|
/// The description of all the contacts between a pair of colliders.
|
|
pub struct ContactPair {
|
|
/// The first collider involved in the contact pair.
|
|
pub collider1: ColliderHandle,
|
|
/// The second collider involved in the contact pair.
|
|
pub collider2: ColliderHandle,
|
|
/// The set of contact manifolds between the two colliders.
|
|
///
|
|
/// All contact manifold contain themselves contact points between the colliders.
|
|
pub manifolds: Vec<ContactManifold>,
|
|
/// Is there any active contact in this contact pair?
|
|
pub has_any_active_contact: bool,
|
|
/// Was a `CollisionEvent::Started` emitted for this collider?
|
|
pub(crate) start_event_emited: bool,
|
|
pub(crate) workspace: Option<ContactManifoldsWorkspace>,
|
|
}
|
|
|
|
impl ContactPair {
|
|
pub(crate) fn new(collider1: ColliderHandle, collider2: ColliderHandle) -> Self {
|
|
Self {
|
|
collider1,
|
|
collider2,
|
|
has_any_active_contact: false,
|
|
manifolds: Vec::new(),
|
|
start_event_emited: false,
|
|
workspace: None,
|
|
}
|
|
}
|
|
|
|
/// Clears all the contacts of this contact pair.
|
|
pub fn clear(&mut self) {
|
|
self.manifolds.clear();
|
|
self.has_any_active_contact = false;
|
|
self.workspace = None;
|
|
}
|
|
|
|
/// The sum of all the impulses applied by contacts on this contact pair.
|
|
pub fn total_impulse(&self) -> Vector<Real> {
|
|
self.manifolds
|
|
.iter()
|
|
.map(|m| m.total_impulse() * m.data.normal)
|
|
.sum()
|
|
}
|
|
|
|
/// The sum of the magnitudes of the contacts on this contact pair.
|
|
pub fn total_impulse_magnitude(&self) -> Real {
|
|
self.manifolds
|
|
.iter()
|
|
.fold(0.0, |a, m| a + m.total_impulse())
|
|
}
|
|
|
|
/// The magnitude and (unit) direction of the maximum impulse on this contact pair.
|
|
pub fn max_impulse(&self) -> (Real, Vector<Real>) {
|
|
let mut result = (0.0, Vector::zeros());
|
|
|
|
for m in &self.manifolds {
|
|
let impulse = m.total_impulse();
|
|
|
|
if impulse > result.0 {
|
|
result = (impulse, m.data.normal);
|
|
}
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
/// Finds the contact with the smallest signed distance.
|
|
///
|
|
/// If the colliders involved in this contact pair are penetrating, then
|
|
/// this returns the contact with the largest penetration depth.
|
|
///
|
|
/// Returns a reference to the contact, as well as the contact manifold
|
|
/// it is part of.
|
|
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_emited = 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_emited = 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))]
|
|
/// A contact manifold between two colliders.
|
|
///
|
|
/// A contact manifold describes a set of contacts between two colliders. All the contact
|
|
/// part of the same contact manifold share the same contact normal and contact kinematics.
|
|
pub struct ContactManifoldData {
|
|
// The following are set by the narrow-phase.
|
|
/// The first rigid-body involved in this contact manifold.
|
|
pub rigid_body1: Option<RigidBodyHandle>,
|
|
/// The second rigid-body involved in this contact manifold.
|
|
pub rigid_body2: Option<RigidBodyHandle>,
|
|
// We put the following fields here to avoids reading the colliders inside of the
|
|
// contact preparation method.
|
|
/// Flags used to control some aspects of the constraints solver for this contact manifold.
|
|
pub solver_flags: SolverFlags,
|
|
/// The world-space contact normal shared by all the contact in this contact manifold.
|
|
// NOTE: read the comment of `solver_contacts` regarding serialization. It applies
|
|
// to this field as well.
|
|
pub normal: Vector<Real>,
|
|
/// The contacts that will be seen by the constraints solver for computing forces.
|
|
// NOTE: unfortunately, we can't ignore this field when serialize
|
|
// the contact manifold data. The reason is that the solver contacts
|
|
// won't be updated for sleeping bodies. So it means that for one
|
|
// frame, we won't have any solver contacts when waking up an island
|
|
// after a deserialization. Not only does this break post-snapshot
|
|
// determinism, but it will also skip constraint resolution for these
|
|
// contacts during one frame.
|
|
//
|
|
// An alternative would be to skip the serialization of `solver_contacts` and
|
|
// find a way to recompute them right after the deserialization process completes.
|
|
// However, this would be an expensive operation. And doing this efficiently as part
|
|
// of the narrow-phase update or the contact manifold collect will likely lead to tricky
|
|
// bugs too.
|
|
//
|
|
// So right now it is best to just serialize this field and keep it that way until it
|
|
// is proven to be actually problematic in real applications (in terms of snapshot size for example).
|
|
pub solver_contacts: Vec<SolverContact>,
|
|
/// The relative dominance of the bodies involved in this contact manifold.
|
|
pub relative_dominance: i16,
|
|
/// A user-defined piece of data.
|
|
pub user_data: u32,
|
|
}
|
|
|
|
/// A contact seen by the constraints solver for computing forces.
|
|
#[derive(Copy, Clone, Debug)]
|
|
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
|
pub struct SolverContact {
|
|
/// The index of the manifold contact used to generate this solver contact.
|
|
pub(crate) contact_id: u8,
|
|
/// The world-space contact point.
|
|
pub point: Point<Real>,
|
|
/// The distance between the two original contacts points along the contact normal.
|
|
/// If negative, this is measures the penetration depth.
|
|
pub dist: Real,
|
|
/// The effective friction coefficient at this contact point.
|
|
pub friction: Real,
|
|
/// The effective restitution coefficient at this contact point.
|
|
pub restitution: Real,
|
|
/// The desired tangent relative velocity at the contact point.
|
|
///
|
|
/// This is set to zero by default. Set to a non-zero value to
|
|
/// simulate, e.g., conveyor belts.
|
|
pub tangent_velocity: Vector<Real>,
|
|
/// Whether or not this contact existed during the last timestep.
|
|
pub is_new: bool,
|
|
}
|
|
|
|
impl SolverContact {
|
|
/// Should we treat this contact as a bouncy contact?
|
|
/// If `true`, use [`Self::restitution`].
|
|
pub fn is_bouncy(&self) -> bool {
|
|
if self.is_new {
|
|
// Treat new collisions as bouncing at first, unless we have zero restitution.
|
|
self.restitution > 0.0
|
|
} else {
|
|
// If the contact is still here one step later, it is now a resting contact.
|
|
// The exception is very high restitutions, which can never rest
|
|
self.restitution >= 1.0
|
|
}
|
|
}
|
|
}
|
|
|
|
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::zeros(),
|
|
solver_contacts: Vec::new(),
|
|
relative_dominance: 0,
|
|
user_data: 0,
|
|
}
|
|
}
|
|
|
|
/// Number of actives contacts, i.e., contacts that will be seen by
|
|
/// the constraints solver.
|
|
#[inline]
|
|
pub fn num_active_contacts(&self) -> usize {
|
|
self.solver_contacts.len()
|
|
}
|
|
}
|
|
|
|
/// Additional methods for the contact manifold.
|
|
pub trait ContactManifoldExt {
|
|
/// Computes the sum of all the impulses applied by contacts from this contact manifold.
|
|
fn total_impulse(&self) -> Real;
|
|
/// Computes the maximum impulse applied by contacts from this contact manifold.
|
|
fn max_impulse(&self) -> Real;
|
|
}
|
|
|
|
impl ContactManifoldExt for ContactManifold {
|
|
fn total_impulse(&self) -> Real {
|
|
self.points.iter().map(|pt| pt.data.impulse).sum()
|
|
}
|
|
|
|
fn max_impulse(&self) -> Real {
|
|
self.points.iter().fold(0.0, |a, pt| a.max(pt.data.impulse))
|
|
}
|
|
}
|