aboutsummaryrefslogtreecommitdiff
path: root/src/geometry
diff options
context:
space:
mode:
authorCrozet Sébastien <developer@crozet.re>2021-02-23 11:24:54 +0100
committerCrozet Sébastien <developer@crozet.re>2021-02-23 11:24:54 +0100
commit00706e8b360e132cb88a7b393dcedadf35403379 (patch)
treee2ab40056da30c614dc94fc0cb852d6207c19043 /src/geometry
parentad5c10672e36f47fbdb0667bccd79c59ff3a97cc (diff)
downloadrapier-00706e8b360e132cb88a7b393dcedadf35403379.tar.gz
rapier-00706e8b360e132cb88a7b393dcedadf35403379.tar.bz2
rapier-00706e8b360e132cb88a7b393dcedadf35403379.zip
Introduce the PhysicsHook trait used for both contact filtering and contact modification.
Diffstat (limited to 'src/geometry')
-rw-r--r--src/geometry/contact_pair.rs14
-rw-r--r--src/geometry/mod.rs2
-rw-r--r--src/geometry/narrow_phase.rs63
-rw-r--r--src/geometry/pair_filter.rs103
4 files changed, 148 insertions, 34 deletions
diff --git a/src/geometry/contact_pair.rs b/src/geometry/contact_pair.rs
index c94a2cf..11e0188 100644
--- a/src/geometry/contact_pair.rs
+++ b/src/geometry/contact_pair.rs
@@ -9,7 +9,10 @@ bitflags::bitflags! {
pub struct SolverFlags: u32 {
/// The constraint solver will take this contact manifold into
/// account for force computation.
- const COMPUTE_IMPULSES = 0b01;
+ const COMPUTE_IMPULSES = 0b001;
+ /// The user-defined physics hooks will be used to
+ /// modify the solver contacts of this contact manifold.
+ const MODIFY_SOLVER_CONTACTS = 0b010;
}
}
@@ -104,6 +107,8 @@ pub struct ContactManifoldData {
/// The contacts that will be seen by the constraints solver for computing forces.
#[cfg_attr(feature = "serde-serialize", serde(skip))]
pub solver_contacts: Vec<SolverContact>,
+ /// A user-defined piece of data.
+ pub user_data: u32,
}
/// A contact seen by the constraints solver for computing forces.
@@ -165,6 +170,7 @@ impl ContactManifoldData {
solver_flags,
normal: Vector::zeros(),
solver_contacts: Vec::new(),
+ user_data: 0,
}
}
@@ -205,9 +211,3 @@ impl ContactManifoldData {
// manifold.data.warmstart_multiplier = Self::min_warmstart_multiplier()
// }
}
-
-/// A contact manifold that can be modified by the user.
-pub struct ModifiableContactManifold<'a> {
- manifold: &'a super::ContactManifold,
- solver_contacts: &'a mut Vec<SolverContact>,
-}
diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs
index 861763e..2997e24 100644
--- a/src/geometry/mod.rs
+++ b/src/geometry/mod.rs
@@ -10,7 +10,7 @@ pub use self::interaction_graph::{
};
pub use self::interaction_groups::InteractionGroups;
pub use self::narrow_phase::NarrowPhase;
-pub use self::pair_filter::{ContactPairFilter, IntersectionPairFilter, PairFilterContext};
+pub use self::pair_filter::{PairFilterContext, PhysicsHooks};
pub use parry::query::TrackedContact;
diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs
index 9513fef..e929e0f 100644
--- a/src/geometry/narrow_phase.rs
+++ b/src/geometry/narrow_phase.rs
@@ -4,10 +4,11 @@ use rayon::prelude::*;
use crate::data::pubsub::Subscription;
use crate::data::Coarena;
use crate::dynamics::{BodyPair, CoefficientCombineRule, RigidBodySet};
+use crate::geometry::pair_filter::{ContactModificationContext, PhysicsHooksFlags};
use crate::geometry::{
BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ContactData, ContactEvent,
- ContactManifoldData, ContactPairFilter, IntersectionEvent, IntersectionPairFilter,
- PairFilterContext, RemovedCollider, SolverContact, SolverFlags,
+ ContactManifoldData, IntersectionEvent, PairFilterContext, PhysicsHooks, RemovedCollider,
+ SolverContact, SolverFlags,
};
use crate::geometry::{ColliderSet, ContactManifold, ContactPair, InteractionGraph};
use crate::math::{Real, Vector};
@@ -387,11 +388,13 @@ impl NarrowPhase {
&mut self,
bodies: &RigidBodySet,
colliders: &ColliderSet,
- pair_filter: Option<&dyn IntersectionPairFilter>,
+ hooks: &dyn PhysicsHooks,
events: &dyn EventHandler,
) {
let nodes = &self.intersection_graph.graph.nodes;
let query_dispatcher = &*self.query_dispatcher;
+ let active_hooks = hooks.active_hooks();
+
par_iter_mut!(&mut self.intersection_graph.graph.edges).for_each(|edge| {
let handle1 = nodes[edge.source().index()].weight;
let handle2 = nodes[edge.target().index()].weight;
@@ -415,12 +418,15 @@ impl NarrowPhase {
return;
}
- if pair_filter.is_none() && !rb1.is_dynamic() && !rb2.is_dynamic() {
+ if !active_hooks.contains(PhysicsHooksFlags::FILTER_INTERSECTION_PAIR)
+ && !rb1.is_dynamic()
+ && !rb2.is_dynamic()
+ {
// Default filtering rule: no intersection between two non-dynamic bodies.
return;
}
- if let Some(filter) = pair_filter {
+ if active_hooks.contains(PhysicsHooksFlags::FILTER_INTERSECTION_PAIR) {
let context = PairFilterContext {
rigid_body1: rb1,
rigid_body2: rb2,
@@ -430,7 +436,7 @@ impl NarrowPhase {
collider2: co2,
};
- if !filter.filter_intersection_pair(&context) {
+ if !hooks.filter_intersection_pair(&context) {
// No intersection allowed.
return;
}
@@ -458,10 +464,11 @@ impl NarrowPhase {
prediction_distance: Real,
bodies: &RigidBodySet,
colliders: &ColliderSet,
- pair_filter: Option<&dyn ContactPairFilter>,
+ hooks: &dyn PhysicsHooks,
events: &dyn EventHandler,
) {
let query_dispatcher = &*self.query_dispatcher;
+ let active_hooks = hooks.active_hooks();
par_iter_mut!(&mut self.contact_graph.graph.edges).for_each(|edge| {
let pair = &mut edge.weight;
@@ -485,12 +492,16 @@ impl NarrowPhase {
return;
}
- if pair_filter.is_none() && !rb1.is_dynamic() && !rb2.is_dynamic() {
+ if !active_hooks.contains(PhysicsHooksFlags::FILTER_CONTACT_PAIR)
+ && !rb1.is_dynamic()
+ && !rb2.is_dynamic()
+ {
// Default filtering rule: no contact between two non-dynamic bodies.
return;
}
- let mut solver_flags = if let Some(filter) = pair_filter {
+ let mut solver_flags = if active_hooks.contains(PhysicsHooksFlags::FILTER_CONTACT_PAIR)
+ {
let context = PairFilterContext {
rigid_body1: rb1,
rigid_body2: rb2,
@@ -500,7 +511,7 @@ impl NarrowPhase {
collider2: co2,
};
- if let Some(solver_flags) = filter.filter_contact_pair(&context) {
+ if let Some(solver_flags) = hooks.filter_contact_pair(&context) {
solver_flags
} else {
// No contact allowed.
@@ -566,13 +577,39 @@ impl NarrowPhase {
data: contact.data,
};
- // TODO: apply the user-defined contact modification/removal, if needed.
-
manifold.data.solver_contacts.push(solver_contact);
has_any_active_contact = true;
- continue;
}
}
+
+ // Apply the user-defined contact modification.
+ if active_hooks.contains(PhysicsHooksFlags::MODIFY_SOLVER_CONTACTS)
+ && manifold
+ .data
+ .solver_flags
+ .contains(SolverFlags::MODIFY_SOLVER_CONTACTS)
+ {
+ let mut modifiable_solver_contacts =
+ std::mem::replace(&mut manifold.data.solver_contacts, Vec::new());
+ let mut modifiable_user_data = manifold.data.user_data;
+
+ let mut context = ContactModificationContext {
+ rigid_body1: rb1,
+ rigid_body2: rb2,
+ collider_handle1: pair.pair.collider1,
+ collider_handle2: pair.pair.collider2,
+ collider1: co1,
+ collider2: co2,
+ manifold,
+ solver_contacts: &mut modifiable_solver_contacts,
+ user_data: &mut modifiable_user_data,
+ };
+
+ hooks.modify_solver_contacts(&mut context);
+
+ manifold.data.solver_contacts = modifiable_solver_contacts;
+ manifold.data.user_data = modifiable_user_data;
+ }
}
if has_any_active_contact != pair.has_any_active_contact {
diff --git a/src/geometry/pair_filter.rs b/src/geometry/pair_filter.rs
index 25f300f..a30145e 100644
--- a/src/geometry/pair_filter.rs
+++ b/src/geometry/pair_filter.rs
@@ -1,5 +1,5 @@
use crate::dynamics::RigidBody;
-use crate::geometry::{Collider, ColliderHandle, SolverFlags};
+use crate::geometry::{Collider, ColliderHandle, ContactManifold, SolverContact, SolverFlags};
/// Context given to custom collision filters to filter-out collisions.
pub struct PairFilterContext<'a> {
@@ -17,14 +17,54 @@ pub struct PairFilterContext<'a> {
pub collider2: &'a Collider,
}
-/// User-defined filter for potential contact pairs detected by the broad-phase.
-///
-/// This can be used to apply custom logic in order to decide whether two colliders
-/// should have their contact computed by the narrow-phase, and if these contact
-/// should be solved by the constraints solver
-pub trait ContactPairFilter: Send + Sync {
+pub struct ContactModificationContext<'a> {
+ /// The first collider involved in the potential collision.
+ pub rigid_body1: &'a RigidBody,
+ /// The first collider involved in the potential collision.
+ pub rigid_body2: &'a RigidBody,
+ /// The first collider involved in the potential collision.
+ pub collider_handle1: ColliderHandle,
+ /// The first collider involved in the potential collision.
+ pub collider_handle2: ColliderHandle,
+ /// The first collider involved in the potential collision.
+ pub collider1: &'a Collider,
+ /// The first collider involved in the potential collision.
+ pub collider2: &'a Collider,
+ /// The contact manifold.
+ pub manifold: &'a ContactManifold,
+ /// The solver contacts that can be modified.
+ pub solver_contacts: &'a mut Vec<SolverContact>,
+ /// User-defined data attached to the manifold.
+ // NOTE: we keep this a &'a mut u32 to emphasize the
+ // fact that this can be modified.
+ pub user_data: &'a mut u32,
+}
+
+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 PhysicsHooksFlags: u32 {
+ /// If set, Rapier will call `PhysicsHooks::filter_contact_pair` whenever relevant.
+ const FILTER_CONTACT_PAIR = 0b0001;
+ /// If set, Rapier will call `PhysicsHooks::filter_intersection_pair` whenever relevant.
+ const FILTER_INTERSECTION_PAIR = 0b0010;
+ /// If set, Rapier will call `PhysicsHooks::modify_solver_contact` whenever relevant.
+ const MODIFY_SOLVER_CONTACTS = 0b0100;
+ }
+}
+
+/// User-defined functions called by the physics engines during one timestep in order to customize its behavior.
+pub trait PhysicsHooks: Send + Sync {
+ /// The sets of hooks that must be taken into account.
+ fn active_hooks(&self) -> PhysicsHooksFlags;
+
/// Applies the contact pair filter.
///
+ /// User-defined filter for potential contact pairs detected by the broad-phase.
+ /// This can be used to apply custom logic in order to decide whether two colliders
+ /// should have their contact computed by the narrow-phase, and if these contact
+ /// should be solved by the constraints solver
+ ///
/// Note that using a contact pair filter will replace the default contact filtering
/// which consists of preventing contact computation between two non-dynamic bodies.
///
@@ -39,15 +79,14 @@ pub trait ContactPairFilter: Send + Sync {
/// `Some(SolverFlags::empty())` then the constraints solver will ignore these
/// contacts.
fn filter_contact_pair(&self, context: &PairFilterContext) -> Option<SolverFlags>;
-}
-/// User-defined filter for potential intersection pairs detected by the broad-phase.
-///
-/// This can be used to apply custom logic in order to decide whether two colliders
-/// should have their intersection computed by the narrow-phase.
-pub trait IntersectionPairFilter: Send + Sync {
/// Applies the intersection pair filter.
///
+ /// User-defined filter for potential intersection pairs detected by the broad-phase.
+ ///
+ /// This can be used to apply custom logic in order to decide whether two colliders
+ /// should have their intersection computed by the narrow-phase.
+ ///
/// Note that using an intersection pair filter will replace the default intersection filtering
/// which consists of preventing intersection computation between two non-dynamic bodies.
///
@@ -58,4 +97,42 @@ pub trait IntersectionPairFilter: Send + Sync {
/// If this return `true` then the narrow-phase will compute intersection
/// information for this pair.
fn filter_intersection_pair(&self, context: &PairFilterContext) -> bool;
+
+ /// Modifies the set of contacts seen by the constraints solver.
+ ///
+ /// By default, the content of `solver_contacts` is computed from `manifold.points`.
+ /// This method will be called on each contact manifold which have the flag `SolverFlags::MODIFY_CONTACTS` set.
+ /// This method can be used to modify the set of solver contacts seen by the constraints solver: contacts
+ /// can be removed and modified.
+ ///
+ /// Note that if all the contacts have to be ignored by the constraint solver, you may simply
+ /// do `context.solver_contacts.clear()`.
+ ///
+ /// Modifying the solver contacts allow you to achieve various effects, including:
+ /// - Simulating conveyor belts by setting the `surface_velocity` of a solver contact.
+ /// - Simulating shapes with multiply materials by modifying the friction and restitution
+ /// coefficient depending of the features in contacts.
+ /// - Simulating one-way platforms depending on the contact normal.
+ ///
+ /// Each contact manifold is given a `u32` user-defined data that is persistent between
+ /// timesteps (as long as the contact manifold exists). This user-defined data is initialized
+ /// as 0 and can be modified in `context.user_data`.
+ fn modify_solver_contacts(&self, context: &mut ContactModificationContext);
+}
+
+impl PhysicsHooks for () {
+ /// The sets of hooks that must be taken into account.
+ fn active_hooks(&self) -> PhysicsHooksFlags {
+ PhysicsHooksFlags::empty()
+ }
+
+ fn filter_contact_pair(&self, _: &PairFilterContext) -> Option<SolverFlags> {
+ None
+ }
+
+ fn filter_intersection_pair(&self, _: &PairFilterContext) -> bool {
+ false
+ }
+
+ fn modify_solver_contacts(&self, _: &mut ContactModificationContext) {}
}