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 {
|
||||
/// The constraint solver will take this contact manifold into
|
||||
/// 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")]
|
||||
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;
|
||||
|
||||
@@ -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,38 @@ 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())
|
||||
|| (rb1.is_sleeping() && rb2.is_sleeping())
|
||||
{
|
||||
// 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 {
|
||||
rigid_body1: rb1,
|
||||
rigid_body2: rb2,
|
||||
collider1: co1,
|
||||
collider2: co2,
|
||||
};
|
||||
|
||||
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 +365,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,8 +377,9 @@ 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())
|
||||
|| (rb1.is_sleeping() && rb2.is_sleeping())
|
||||
{
|
||||
// No need to update this contact because nothing moved.
|
||||
return;
|
||||
@@ -364,6 +390,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 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;
|
||||
if pair.generator.is_none() {
|
||||
// We need a redispatch for this generator.
|
||||
@@ -374,12 +427,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,
|
||||
@@ -413,8 +460,11 @@ impl NarrowPhase {
|
||||
for manifold in &mut inter.weight.manifolds {
|
||||
let rb1 = &bodies[manifold.body_pair.body1];
|
||||
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
|
||||
&& (rb1.is_dynamic() || rb2.is_dynamic())
|
||||
&& (!rb1.is_dynamic() || !rb1.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.
|
||||
|
||||
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,
|
||||
|
||||
@@ -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);
|
||||
@@ -285,6 +290,8 @@ mod test {
|
||||
&mut bodies,
|
||||
&mut colliders,
|
||||
&mut joints,
|
||||
None,
|
||||
None,
|
||||
&(),
|
||||
);
|
||||
}
|
||||
@@ -327,6 +334,8 @@ mod test {
|
||||
&mut bodies,
|
||||
&mut colliders,
|
||||
&mut joints,
|
||||
None,
|
||||
None,
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user