Added user-implementable traits for collision/proximity pair filtering.

This commit is contained in:
Crozet Sébastien
2020-10-27 16:11:20 +01:00
parent a52fb8d7e4
commit cc44b65094
6 changed files with 150 additions and 18 deletions

View File

@@ -22,6 +22,7 @@ pub use self::proximity_detector::{DefaultProximityDispatcher, ProximityDispatch
#[cfg(feature = "dim3")] #[cfg(feature = "dim3")]
pub use self::round_cylinder::RoundCylinder; pub use self::round_cylinder::RoundCylinder;
pub use self::trimesh::Trimesh; pub use self::trimesh::Trimesh;
pub use self::user_callbacks::{ContactPairFilter, PairFilterContext, ProximityPairFilter};
pub use ncollide::query::Proximity; pub use ncollide::query::Proximity;
/// A segment shape. /// A segment shape.
@@ -104,3 +105,4 @@ mod polygonal_feature_map;
#[cfg(feature = "dim3")] #[cfg(feature = "dim3")]
mod round_cylinder; mod round_cylinder;
mod shape; mod shape;
mod user_callbacks;

View File

@@ -14,8 +14,9 @@ use crate::geometry::proximity_detector::{
// proximity_detector::ProximityDetectionContextSimd, WBall, // proximity_detector::ProximityDetectionContextSimd, WBall,
//}; //};
use crate::geometry::{ use crate::geometry::{
BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ContactEvent, ProximityEvent, BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ContactEvent, ContactPairFilter,
ProximityPair, RemovedCollider, SolverFlags, PairFilterContext, ProximityEvent, ProximityPair, ProximityPairFilter, RemovedCollider,
SolverFlags,
}; };
use crate::geometry::{ColliderSet, ContactManifold, ContactPair, InteractionGraph}; use crate::geometry::{ColliderSet, ContactManifold, ContactPair, InteractionGraph};
//#[cfg(feature = "simd-is-enabled")] //#[cfg(feature = "simd-is-enabled")]
@@ -290,6 +291,7 @@ impl NarrowPhase {
prediction_distance: f32, prediction_distance: f32,
bodies: &RigidBodySet, bodies: &RigidBodySet,
colliders: &ColliderSet, colliders: &ColliderSet,
pair_filter: Option<&dyn ProximityPairFilter>,
events: &dyn EventHandler, events: &dyn EventHandler,
) { ) {
par_iter_mut!(&mut self.proximity_graph.graph.edges).for_each(|edge| { par_iter_mut!(&mut self.proximity_graph.graph.edges).for_each(|edge| {
@@ -301,16 +303,35 @@ impl NarrowPhase {
let rb1 = &bodies[co1.parent]; let rb1 = &bodies[co1.parent];
let rb2 = &bodies[co2.parent]; let rb2 = &bodies[co2.parent];
if (rb1.is_sleeping() || rb1.is_static()) && (rb2.is_sleeping() || rb2.is_static()) { if (rb1.is_sleeping() && rb2.is_static()) || (rb2.is_sleeping() && rb1.is_static()) {
// No need to update this contact because nothing moved. // No need to update this proximity because nothing moved.
return; return;
} }
if !co1.collision_groups.test(co2.collision_groups) { if !co1.collision_groups.test(co2.collision_groups) {
// The collision is not allowed. // The proximity is not allowed.
return; 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; let dispatcher = DefaultProximityDispatcher;
if pair.detector.is_none() { if pair.detector.is_none() {
// We need a redispatch for this detector. // We need a redispatch for this detector.
@@ -341,6 +362,7 @@ impl NarrowPhase {
prediction_distance: f32, prediction_distance: f32,
bodies: &RigidBodySet, bodies: &RigidBodySet,
colliders: &ColliderSet, colliders: &ColliderSet,
pair_filter: Option<&dyn ContactPairFilter>,
events: &dyn EventHandler, events: &dyn EventHandler,
) { ) {
par_iter_mut!(&mut self.contact_graph.graph.edges).for_each(|edge| { par_iter_mut!(&mut self.contact_graph.graph.edges).for_each(|edge| {
@@ -352,9 +374,7 @@ impl NarrowPhase {
let rb1 = &bodies[co1.parent]; let rb1 = &bodies[co1.parent];
let rb2 = &bodies[co2.parent]; let rb2 = &bodies[co2.parent];
if ((rb1.is_sleeping() || rb1.is_static()) && (rb2.is_sleeping() || rb2.is_static())) if (rb1.is_sleeping() && rb2.is_static()) || (rb2.is_sleeping() && rb1.is_static()) {
|| (!rb1.is_dynamic() && !rb2.is_dynamic())
{
// No need to update this contact because nothing moved. // No need to update this contact because nothing moved.
return; return;
} }
@@ -364,6 +384,33 @@ impl NarrowPhase {
return; 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; let dispatcher = DefaultContactDispatcher;
if pair.generator.is_none() { if pair.generator.is_none() {
// We need a redispatch for this generator. // We need a redispatch for this generator.
@@ -374,12 +421,6 @@ impl NarrowPhase {
pair.generator_workspace = workspace; pair.generator_workspace = workspace;
} }
let solver_flags = if co1.solver_groups.test(co2.solver_groups) {
SolverFlags::COMPUTE_FORCES
} else {
SolverFlags::empty()
};
let context = ContactGenerationContext { let context = ContactGenerationContext {
dispatcher: &dispatcher, dispatcher: &dispatcher,
prediction_distance, prediction_distance,
@@ -415,6 +456,7 @@ impl NarrowPhase {
let rb2 = &bodies[manifold.body_pair.body2]; let rb2 = &bodies[manifold.body_pair.body2];
if manifold.solver_flags.contains(SolverFlags::COMPUTE_FORCES) if manifold.solver_flags.contains(SolverFlags::COMPUTE_FORCES)
&& manifold.num_active_contacts() != 0 && manifold.num_active_contacts() != 0
&& (rb1.is_dynamic() || rb2.is_dynamic())
&& (!rb1.is_dynamic() || !rb1.is_sleeping()) && (!rb1.is_dynamic() || !rb1.is_sleeping())
&& (!rb2.is_dynamic() || !rb2.is_sleeping()) && (!rb2.is_dynamic() || !rb2.is_sleeping())
{ {

View File

@@ -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;
}

View File

@@ -1,7 +1,10 @@
//! Physics pipeline structures. //! Physics pipeline structures.
use crate::dynamics::{JointSet, RigidBodySet}; 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; use crate::pipeline::EventHandler;
/// The collision pipeline, responsible for performing collision detection between colliders. /// The collision pipeline, responsible for performing collision detection between colliders.
@@ -40,6 +43,8 @@ impl CollisionPipeline {
narrow_phase: &mut NarrowPhase, narrow_phase: &mut NarrowPhase,
bodies: &mut RigidBodySet, bodies: &mut RigidBodySet,
colliders: &mut ColliderSet, colliders: &mut ColliderSet,
contact_pair_filter: Option<&dyn ContactPairFilter>,
proximity_pair_filter: Option<&dyn ProximityPairFilter>,
events: &dyn EventHandler, events: &dyn EventHandler,
) { ) {
bodies.maintain_active_set(); bodies.maintain_active_set();
@@ -52,8 +57,20 @@ impl CollisionPipeline {
narrow_phase.register_pairs(colliders, bodies, &self.broad_phase_events, events); narrow_phase.register_pairs(colliders, bodies, &self.broad_phase_events, events);
narrow_phase.compute_contacts(prediction_distance, bodies, colliders, events); narrow_phase.compute_contacts(
narrow_phase.compute_proximities(prediction_distance, bodies, colliders, events); 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( bodies.update_active_set_with_contacts(
colliders, colliders,

View File

@@ -7,7 +7,8 @@ use crate::dynamics::{IntegrationParameters, JointSet, RigidBodySet};
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
use crate::dynamics::{JointGraphEdge, ParallelIslandSolver as IslandSolver}; use crate::dynamics::{JointGraphEdge, ParallelIslandSolver as IslandSolver};
use crate::geometry::{ use crate::geometry::{
BroadPhase, BroadPhasePairEvent, ColliderPair, ColliderSet, ContactManifoldIndex, NarrowPhase, BroadPhase, BroadPhasePairEvent, ColliderPair, ColliderSet, ContactManifoldIndex,
ContactPairFilter, NarrowPhase, ProximityPairFilter,
}; };
use crate::math::Vector; use crate::math::Vector;
use crate::pipeline::EventHandler; use crate::pipeline::EventHandler;
@@ -68,6 +69,8 @@ impl PhysicsPipeline {
bodies: &mut RigidBodySet, bodies: &mut RigidBodySet,
colliders: &mut ColliderSet, colliders: &mut ColliderSet,
joints: &mut JointSet, joints: &mut JointSet,
contact_pair_filter: Option<&dyn ContactPairFilter>,
proximity_pair_filter: Option<&dyn ProximityPairFilter>,
events: &dyn EventHandler, events: &dyn EventHandler,
) { ) {
self.counters.step_started(); self.counters.step_started();
@@ -112,12 +115,14 @@ impl PhysicsPipeline {
integration_parameters.prediction_distance, integration_parameters.prediction_distance,
bodies, bodies,
colliders, colliders,
contact_pair_filter,
events, events,
); );
narrow_phase.compute_proximities( narrow_phase.compute_proximities(
integration_parameters.prediction_distance, integration_parameters.prediction_distance,
bodies, bodies,
colliders, colliders,
proximity_pair_filter,
events, events,
); );
// println!("Compute contact time: {}", instant::now() - t); // println!("Compute contact time: {}", instant::now() - t);

View File

@@ -681,6 +681,8 @@ impl Testbed {
&mut self.physics.bodies, &mut self.physics.bodies,
&mut self.physics.colliders, &mut self.physics.colliders,
&mut self.physics.joints, &mut self.physics.joints,
None,
None,
&self.event_handler, &self.event_handler,
); );
@@ -1457,6 +1459,8 @@ impl State for Testbed {
&mut physics.bodies, &mut physics.bodies,
&mut physics.colliders, &mut physics.colliders,
&mut physics.joints, &mut physics.joints,
None,
None,
event_handler, event_handler,
); );
}); });
@@ -1471,6 +1475,8 @@ impl State for Testbed {
&mut self.physics.bodies, &mut self.physics.bodies,
&mut self.physics.colliders, &mut self.physics.colliders,
&mut self.physics.joints, &mut self.physics.joints,
None,
None,
&self.event_handler, &self.event_handler,
); );