aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/geometry/mod.rs2
-rw-r--r--src/geometry/narrow_phase.rs70
-rw-r--r--src/geometry/user_callbacks.rs60
-rw-r--r--src/pipeline/collision_pipeline.rs23
-rw-r--r--src/pipeline/physics_pipeline.rs7
-rw-r--r--src_testbed/testbed.rs6
6 files changed, 150 insertions, 18 deletions
diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs
index 9da35d9..c8ad28e 100644
--- a/src/geometry/mod.rs
+++ b/src/geometry/mod.rs
@@ -22,6 +22,7 @@ pub use self::proximity_detector::{DefaultProximityDispatcher, ProximityDispatch
#[cfg(feature = "dim3")]
pub use self::round_cylinder::RoundCylinder;
pub use self::trimesh::Trimesh;
+pub use self::user_callbacks::{ContactPairFilter, PairFilterContext, ProximityPairFilter};
pub use ncollide::query::Proximity;
/// A segment shape.
@@ -104,3 +105,4 @@ mod polygonal_feature_map;
#[cfg(feature = "dim3")]
mod round_cylinder;
mod shape;
+mod user_callbacks;
diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs
index f5517ba..fb5da99 100644
--- a/src/geometry/narrow_phase.rs
+++ b/src/geometry/narrow_phase.rs
@@ -14,8 +14,9 @@ use crate::geometry::proximity_detector::{
// proximity_detector::ProximityDetectionContextSimd, WBall,
//};
use crate::geometry::{
- BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ContactEvent, ProximityEvent,
- ProximityPair, RemovedCollider, SolverFlags,
+ BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ContactEvent, ContactPairFilter,
+ PairFilterContext, ProximityEvent, ProximityPair, ProximityPairFilter, RemovedCollider,
+ SolverFlags,
};
use crate::geometry::{ColliderSet, ContactManifold, ContactPair, InteractionGraph};
//#[cfg(feature = "simd-is-enabled")]
@@ -290,6 +291,7 @@ impl NarrowPhase {
prediction_distance: f32,
bodies: &RigidBodySet,
colliders: &ColliderSet,
+ pair_filter: Option<&dyn ProximityPairFilter>,
events: &dyn EventHandler,
) {
par_iter_mut!(&mut self.proximity_graph.graph.edges).for_each(|edge| {
@@ -301,16 +303,35 @@ impl NarrowPhase {
let rb1 = &bodies[co1.parent];
let rb2 = &bodies[co2.parent];
- if (rb1.is_sleeping() || rb1.is_static()) && (rb2.is_sleeping() || rb2.is_static()) {
- // No need to update this contact because nothing moved.
+ if (rb1.is_sleeping() && rb2.is_static()) || (rb2.is_sleeping() && rb1.is_static()) {
+ // No need to update this proximity because nothing moved.
return;
}
if !co1.collision_groups.test(co2.collision_groups) {
- // The collision is not allowed.
+ // The proximity is not allowed.
+ return;
+ }
+
+ if pair_filter.is_none() && !rb1.is_dynamic() && !rb2.is_dynamic() {
+ // Default filtering rule: no proximity between two non-dynamic bodies.
return;
}
+ if let Some(filter) = pair_filter {
+ let context = PairFilterContext {
+ collider1: co1,
+ collider2: co2,
+ rigid_body1: rb1,
+ rigid_body2: rb2,
+ };
+
+ if !filter.filter_proximity_pair(&context) {
+ // No proximity allowed.
+ return;
+ }
+ }
+
let dispatcher = DefaultProximityDispatcher;
if pair.detector.is_none() {
// We need a redispatch for this detector.
@@ -341,6 +362,7 @@ impl NarrowPhase {
prediction_distance: f32,
bodies: &RigidBodySet,
colliders: &ColliderSet,
+ pair_filter: Option<&dyn ContactPairFilter>,
events: &dyn EventHandler,
) {
par_iter_mut!(&mut self.contact_graph.graph.edges).for_each(|edge| {
@@ -352,9 +374,7 @@ impl NarrowPhase {
let rb1 = &bodies[co1.parent];
let rb2 = &bodies[co2.parent];
- if ((rb1.is_sleeping() || rb1.is_static()) && (rb2.is_sleeping() || rb2.is_static()))
- || (!rb1.is_dynamic() && !rb2.is_dynamic())
- {
+ if (rb1.is_sleeping() && rb2.is_static()) || (rb2.is_sleeping() && rb1.is_static()) {
// No need to update this contact because nothing moved.
return;
}
@@ -364,6 +384,33 @@ impl NarrowPhase {
return;
}
+ if pair_filter.is_none() && !rb1.is_dynamic() && !rb2.is_dynamic() {
+ // Default filtering rule: no contact between two non-dynamic bodies.
+ return;
+ }
+
+ let solver_flags = if let Some(filter) = pair_filter {
+ let context = PairFilterContext {
+ collider1: co1,
+ collider2: co2,
+ rigid_body1: rb1,
+ rigid_body2: rb2,
+ };
+
+ if let Some(solver_flags) = filter.filter_contact_pair(&context) {
+ solver_flags
+ } else {
+ // No contact allowed.
+ return;
+ }
+ } else {
+ if co1.solver_groups.test(co2.solver_groups) {
+ SolverFlags::COMPUTE_FORCES
+ } else {
+ SolverFlags::empty()
+ }
+ };
+
let dispatcher = DefaultContactDispatcher;
if pair.generator.is_none() {
// We need a redispatch for this generator.
@@ -374,12 +421,6 @@ impl NarrowPhase {
pair.generator_workspace = workspace;
}
- let solver_flags = if co1.solver_groups.test(co2.solver_groups) {
- SolverFlags::COMPUTE_FORCES
- } else {
- SolverFlags::empty()
- };
-
let context = ContactGenerationContext {
dispatcher: &dispatcher,
prediction_distance,
@@ -415,6 +456,7 @@ impl NarrowPhase {
let rb2 = &bodies[manifold.body_pair.body2];
if manifold.solver_flags.contains(SolverFlags::COMPUTE_FORCES)
&& manifold.num_active_contacts() != 0
+ && (rb1.is_dynamic() || rb2.is_dynamic())
&& (!rb1.is_dynamic() || !rb1.is_sleeping())
&& (!rb2.is_dynamic() || !rb2.is_sleeping())
{
diff --git a/src/geometry/user_callbacks.rs b/src/geometry/user_callbacks.rs
new file mode 100644
index 0000000..3602faf
--- /dev/null
+++ b/src/geometry/user_callbacks.rs
@@ -0,0 +1,60 @@
+use crate::dynamics::RigidBody;
+use crate::geometry::{Collider, SolverFlags};
+
+/// Context given to custom collision filters to filter-out collisions.
+pub struct PairFilterContext<'a> {
+ /// 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 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,
+}
+
+/// 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 {
+ /// Applies the contact pair filter.
+ ///
+ /// Note that using a contact pair filter will replace the default contact filtering
+ /// which consists of preventing contact computation between two non-dynamic bodies.
+ ///
+ /// Note that using a contact pair filter will replace the default determination
+ /// of solver flags, based on the colliders solver groups.
+ ///
+ /// This filtering method is called after taking into account the colliders collision groups.
+ ///
+ /// If this returns `None`, then the narrow-phase will ignore this contact pair and
+ /// not compute any contact manifolds for it.
+ /// If this returns `Some`, then the narrow-phase will compute contact manifolds for
+ /// this pair of colliders, and configure them with the returned solver flags. For
+ /// example, if this returns `Some(SolverFlags::COMPUTE_FORCES)` then the contacts
+ /// will be taken into account by the constraints solver. If this returns
+ /// `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 proximity pairs detected by the broad-phase.
+///
+/// This can be used to apply custom logic in order to decide whether two colliders
+/// should have their proximity computed by the narrow-phase.
+pub trait ProximityPairFilter: Send + Sync {
+ /// Applies the proximity pair filter.
+ ///
+ /// Note that using a proximity pair filter will replace the default proximity filtering
+ /// which consists of preventing proximity computation between two non-dynamic bodies.
+ ///
+ /// This filtering method is called after taking into account the colliders collision groups.
+ ///
+ /// If this returns `false`, then the narrow-phase will ignore this pair and
+ /// not compute any proximity information for it.
+ /// If this return `true` then the narrow-phase will compute proximity
+ /// information for this pair.
+ fn filter_proximity_pair(&self, context: &PairFilterContext) -> bool;
+}
diff --git a/src/pipeline/collision_pipeline.rs b/src/pipeline/collision_pipeline.rs
index 5a19e52..b8896e8 100644
--- a/src/pipeline/collision_pipeline.rs
+++ b/src/pipeline/collision_pipeline.rs
@@ -1,7 +1,10 @@
//! Physics pipeline structures.
use crate::dynamics::{JointSet, RigidBodySet};
-use crate::geometry::{BroadPhase, BroadPhasePairEvent, ColliderPair, ColliderSet, NarrowPhase};
+use crate::geometry::{
+ BroadPhase, BroadPhasePairEvent, ColliderPair, ColliderSet, ContactPairFilter, NarrowPhase,
+ ProximityPairFilter,
+};
use crate::pipeline::EventHandler;
/// The collision pipeline, responsible for performing collision detection between colliders.
@@ -40,6 +43,8 @@ impl CollisionPipeline {
narrow_phase: &mut NarrowPhase,
bodies: &mut RigidBodySet,
colliders: &mut ColliderSet,
+ contact_pair_filter: Option<&dyn ContactPairFilter>,
+ proximity_pair_filter: Option<&dyn ProximityPairFilter>,
events: &dyn EventHandler,
) {
bodies.maintain_active_set();
@@ -52,8 +57,20 @@ impl CollisionPipeline {
narrow_phase.register_pairs(colliders, bodies, &self.broad_phase_events, events);
- narrow_phase.compute_contacts(prediction_distance, bodies, colliders, events);
- narrow_phase.compute_proximities(prediction_distance, bodies, colliders, events);
+ narrow_phase.compute_contacts(
+ prediction_distance,
+ bodies,
+ colliders,
+ contact_pair_filter,
+ events,
+ );
+ narrow_phase.compute_proximities(
+ prediction_distance,
+ bodies,
+ colliders,
+ proximity_pair_filter,
+ events,
+ );
bodies.update_active_set_with_contacts(
colliders,
diff --git a/src/pipeline/physics_pipeline.rs b/src/pipeline/physics_pipeline.rs
index 47fd260..b99934b 100644
--- a/src/pipeline/physics_pipeline.rs
+++ b/src/pipeline/physics_pipeline.rs
@@ -7,7 +7,8 @@ use crate::dynamics::{IntegrationParameters, JointSet, RigidBodySet};
#[cfg(feature = "parallel")]
use crate::dynamics::{JointGraphEdge, ParallelIslandSolver as IslandSolver};
use crate::geometry::{
- BroadPhase, BroadPhasePairEvent, ColliderPair, ColliderSet, ContactManifoldIndex, NarrowPhase,
+ BroadPhase, BroadPhasePairEvent, ColliderPair, ColliderSet, ContactManifoldIndex,
+ ContactPairFilter, NarrowPhase, ProximityPairFilter,
};
use crate::math::Vector;
use crate::pipeline::EventHandler;
@@ -68,6 +69,8 @@ impl PhysicsPipeline {
bodies: &mut RigidBodySet,
colliders: &mut ColliderSet,
joints: &mut JointSet,
+ contact_pair_filter: Option<&dyn ContactPairFilter>,
+ proximity_pair_filter: Option<&dyn ProximityPairFilter>,
events: &dyn EventHandler,
) {
self.counters.step_started();
@@ -112,12 +115,14 @@ impl PhysicsPipeline {
integration_parameters.prediction_distance,
bodies,
colliders,
+ contact_pair_filter,
events,
);
narrow_phase.compute_proximities(
integration_parameters.prediction_distance,
bodies,
colliders,
+ proximity_pair_filter,
events,
);
// println!("Compute contact time: {}", instant::now() - t);
diff --git a/src_testbed/testbed.rs b/src_testbed/testbed.rs
index 456b894..3bf720a 100644
--- a/src_testbed/testbed.rs
+++ b/src_testbed/testbed.rs
@@ -681,6 +681,8 @@ impl Testbed {
&mut self.physics.bodies,
&mut self.physics.colliders,
&mut self.physics.joints,
+ None,
+ None,
&self.event_handler,
);
@@ -1457,6 +1459,8 @@ impl State for Testbed {
&mut physics.bodies,
&mut physics.colliders,
&mut physics.joints,
+ None,
+ None,
event_handler,
);
});
@@ -1471,6 +1475,8 @@ impl State for Testbed {
&mut self.physics.bodies,
&mut self.physics.colliders,
&mut self.physics.joints,
+ None,
+ None,
&self.event_handler,
);