Merge pull request #44 from dimforge/custom_callbacks_filtering
Added user-implementable traits for collision/proximity pair filtering.
This commit is contained in:
@@ -15,7 +15,7 @@ bitflags::bitflags! {
|
|||||||
pub struct SolverFlags: u32 {
|
pub struct SolverFlags: u32 {
|
||||||
/// The constraint solver will take this contact manifold into
|
/// The constraint solver will take this contact manifold into
|
||||||
/// account for force computation.
|
/// account for force computation.
|
||||||
const COMPUTE_FORCES = 0b01;
|
const COMPUTE_IMPULSES = 0b01;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,38 @@ 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())
|
||||||
// No need to update this contact because nothing moved.
|
|| (rb2.is_sleeping() && rb1.is_static())
|
||||||
|
|| (rb1.is_sleeping() && rb2.is_sleeping())
|
||||||
|
{
|
||||||
|
// 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 {
|
||||||
|
rigid_body1: rb1,
|
||||||
|
rigid_body2: rb2,
|
||||||
|
collider1: co1,
|
||||||
|
collider2: co2,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 +365,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,8 +377,9 @@ 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())
|
||||||
|| (!rb1.is_dynamic() && !rb2.is_dynamic())
|
|| (rb2.is_sleeping() && rb1.is_static())
|
||||||
|
|| (rb1.is_sleeping() && rb2.is_sleeping())
|
||||||
{
|
{
|
||||||
// No need to update this contact because nothing moved.
|
// No need to update this contact because nothing moved.
|
||||||
return;
|
return;
|
||||||
@@ -364,6 +390,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 mut solver_flags = if let Some(filter) = pair_filter {
|
||||||
|
let context = PairFilterContext {
|
||||||
|
rigid_body1: rb1,
|
||||||
|
rigid_body2: rb2,
|
||||||
|
collider1: co1,
|
||||||
|
collider2: co2,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(solver_flags) = filter.filter_contact_pair(&context) {
|
||||||
|
solver_flags
|
||||||
|
} else {
|
||||||
|
// No contact allowed.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SolverFlags::COMPUTE_IMPULSES
|
||||||
|
};
|
||||||
|
|
||||||
|
if !co1.solver_groups.test(co2.solver_groups) {
|
||||||
|
solver_flags.remove(SolverFlags::COMPUTE_IMPULSES);
|
||||||
|
}
|
||||||
|
|
||||||
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 +427,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,
|
||||||
@@ -413,8 +460,11 @@ impl NarrowPhase {
|
|||||||
for manifold in &mut inter.weight.manifolds {
|
for manifold in &mut inter.weight.manifolds {
|
||||||
let rb1 = &bodies[manifold.body_pair.body1];
|
let rb1 = &bodies[manifold.body_pair.body1];
|
||||||
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_IMPULSES)
|
||||||
&& 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())
|
||||||
{
|
{
|
||||||
|
|||||||
57
src/geometry/user_callbacks.rs
Normal file
57
src/geometry/user_callbacks.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
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 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 collider1: &'a Collider,
|
||||||
|
/// The first collider involved in the potential collision.
|
||||||
|
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 {
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// 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_IMPULSES)` 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;
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -285,6 +290,8 @@ mod test {
|
|||||||
&mut bodies,
|
&mut bodies,
|
||||||
&mut colliders,
|
&mut colliders,
|
||||||
&mut joints,
|
&mut joints,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
&(),
|
&(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -327,6 +334,8 @@ mod test {
|
|||||||
&mut bodies,
|
&mut bodies,
|
||||||
&mut colliders,
|
&mut colliders,
|
||||||
&mut joints,
|
&mut joints,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
&(),
|
&(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user