Add collision groups to filter collision pairs.

This commit is contained in:
Crozet Sébastien
2020-10-27 12:08:46 +01:00
parent c336ae6455
commit 3def91d62e
4 changed files with 87 additions and 129 deletions

View File

@@ -1,7 +1,7 @@
use crate::dynamics::{MassProperties, RigidBodyHandle, RigidBodySet}; use crate::dynamics::{MassProperties, RigidBodyHandle, RigidBodySet};
use crate::geometry::{ use crate::geometry::{
Ball, Capsule, ColliderGraphIndex, Contact, Cuboid, HeightField, InteractionGraph, Proximity, Ball, Capsule, ColliderGraphIndex, Contact, Cuboid, HeightField, InteractionGraph,
Segment, Shape, ShapeType, Triangle, Trimesh, InteractionGroups, Proximity, Segment, Shape, ShapeType, Triangle, Trimesh,
}; };
#[cfg(feature = "dim3")] #[cfg(feature = "dim3")]
use crate::geometry::{Cone, Cylinder, RoundCylinder}; use crate::geometry::{Cone, Cylinder, RoundCylinder};
@@ -203,6 +203,7 @@ pub struct Collider {
pub friction: f32, pub friction: f32,
/// The restitution coefficient of this collider. /// The restitution coefficient of this collider.
pub restitution: f32, pub restitution: f32,
pub(crate) collision_groups: InteractionGroups,
pub(crate) contact_graph_index: ColliderGraphIndex, pub(crate) contact_graph_index: ColliderGraphIndex,
pub(crate) proximity_graph_index: ColliderGraphIndex, pub(crate) proximity_graph_index: ColliderGraphIndex,
pub(crate) proxy_index: usize, pub(crate) proxy_index: usize,
@@ -255,6 +256,11 @@ impl Collider {
&self.delta &self.delta
} }
/// The collision groups used by this collider.
pub fn collision_groups(&self) -> InteractionGroups {
self.collision_groups
}
/// The density of this collider. /// The density of this collider.
pub fn density(&self) -> f32 { pub fn density(&self) -> f32 {
self.density self.density
@@ -300,6 +306,8 @@ pub struct ColliderBuilder {
pub is_sensor: bool, pub is_sensor: bool,
/// The user-data of the collider beind built. /// The user-data of the collider beind built.
pub user_data: u128, pub user_data: u128,
/// The collision groups for the collider being built.
pub collision_groups: InteractionGroups,
} }
impl ColliderBuilder { impl ColliderBuilder {
@@ -313,6 +321,7 @@ impl ColliderBuilder {
delta: Isometry::identity(), delta: Isometry::identity(),
is_sensor: false, is_sensor: false,
user_data: 0, user_data: 0,
collision_groups: InteractionGroups::all(),
} }
} }
@@ -418,12 +427,21 @@ impl ColliderBuilder {
0.5 0.5
} }
/// An arbitrary user-defined 128-bit integer associated to the colliders built by this builder. /// Sets an arbitrary user-defined 128-bit integer associated to the colliders built by this builder.
pub fn user_data(mut self, data: u128) -> Self { pub fn user_data(mut self, data: u128) -> Self {
self.user_data = data; self.user_data = data;
self self
} }
/// Sets the collision groups used by this collider.
///
/// Two colliders will interact iff. their collision groups are compatible.
/// See [InteractionGroups::test] for details.
pub fn collision_groups(mut self, groups: InteractionGroups) -> Self {
self.collision_groups = groups;
self
}
/// Sets whether or not the collider built by this builder is a sensor. /// Sets whether or not the collider built by this builder is a sensor.
pub fn sensor(mut self, is_sensor: bool) -> Self { pub fn sensor(mut self, is_sensor: bool) -> Self {
self.is_sensor = is_sensor; self.is_sensor = is_sensor;

View File

@@ -0,0 +1,54 @@
#[repr(transparent)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
/// Pairwise filtering using bit masks.
///
/// This filtering method is based on two 16-bit values:
/// - The interaction groups (the 16 left-most bits of `self.0`).
/// - The interaction mask (the 16 right-most bits of `self.0`).
///
/// An interaction is allowed between two filters `a` and `b` two conditions
/// are met simultaneously:
/// - The interaction groups of `a` has at least one bit set to `1` in common with the interaction mask of `b`.
/// - The interaction groups of `b` has at least one bit set to `1` in common with the interaction mask of `a`.
/// In other words, interactions are allowed between two filter iff. the following condition is met:
/// ```rust
/// ((self.0 >> 16) & rhs.0) != 0 && ((rhs.0 >> 16) & self.0) != 0
/// ```
pub struct InteractionGroups(pub u32);
impl InteractionGroups {
/// Allow interaction with everything.
pub fn all() -> Self {
Self(u32::MAX)
}
/// Prevent all interactions.
pub fn none() -> Self {
Self(0)
}
/// Sets the group this filter is part of.
pub fn with_groups(self, groups: u16) -> Self {
Self((self.0 & 0x0000ffff) | ((groups as u32) << 16))
}
/// Sets the interaction mask of this filter.
pub fn with_mask(self, mask: u16) -> Self {
Self((self.0 & 0xffff0000) | (mask as u32))
}
/// Check if interactions should be allowed based on the interaction groups and mask.
///
/// An interaction is allowed iff. the groups of `self` contain at least one bit set to 1 in common
/// with the mask of `rhs`, and vice-versa.
pub fn test(self, rhs: Self) -> bool {
((self.0 >> 16) & rhs.0) != 0 && ((rhs.0 >> 16) & self.0) != 0
}
}
impl Default for InteractionGroups {
fn default() -> Self {
Self::all()
}
}

View File

@@ -70,6 +70,7 @@ pub(crate) use self::polyhedron_feature3d::PolyhedronFace;
pub(crate) use self::waabb::{WRay, WAABB}; pub(crate) use self::waabb::{WRay, WAABB};
pub(crate) use self::wquadtree::WQuadtree; pub(crate) use self::wquadtree::WQuadtree;
//pub(crate) use self::z_order::z_cmp_floats; //pub(crate) use self::z_order::z_cmp_floats;
pub use self::interaction_groups::InteractionGroups;
pub use self::shape::{Shape, ShapeType}; pub use self::shape::{Shape, ShapeType};
mod ball; mod ball;
@@ -97,6 +98,7 @@ mod waabb;
mod wquadtree; mod wquadtree;
//mod z_order; //mod z_order;
mod capsule; mod capsule;
mod interaction_groups;
#[cfg(feature = "dim3")] #[cfg(feature = "dim3")]
mod polygonal_feature_map; mod polygonal_feature_map;
#[cfg(feature = "dim3")] #[cfg(feature = "dim3")]

View File

@@ -306,6 +306,11 @@ impl NarrowPhase {
return; return;
} }
if !co1.collision_groups.test(co2.collision_groups) {
// The collision is not 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.
@@ -329,69 +334,6 @@ impl NarrowPhase {
.unwrap() .unwrap()
.detect_proximity(context, events); .detect_proximity(context, events);
}); });
/*
// First, group pairs.
// NOTE: the transmutes here are OK because the Vec are all cleared
// before we leave this method.
// We do this in order to avoid reallocating those vecs each time
// we compute the contacts. Unsafe is necessary because we can't just
// store a Vec<&mut ProximityPair> into the NarrowPhase struct without
// polluting the World with lifetimes.
let ball_ball_prox: &mut Vec<&mut ProximityPair> =
unsafe { std::mem::transmute(&mut self.ball_ball_prox) };
let shape_shape_prox: &mut Vec<&mut ProximityPair> =
unsafe { std::mem::transmute(&mut self.shape_shape_prox) };
let bodies = &bodies.bodies;
// FIXME: don't iterate through all the interactions.
for pair in &mut self.proximity_graph.interactions {
let co1 = &colliders[pair.pair.collider1];
let co2 = &colliders[pair.pair.collider2];
// FIXME: avoid lookup into bodies.
let rb1 = &bodies[co1.parent];
let rb2 = &bodies[co2.parent];
if (rb1.is_sleeping() || !rb1.is_dynamic()) && (rb2.is_sleeping() || !rb2.is_dynamic())
{
// No need to update this proximity because nothing moved.
continue;
}
match (co1.shape(), co2.shape()) {
(Shape::Ball(_), Shape::Ball(_)) => ball_ball_prox.push(pair),
_ => shape_shape_prox.push(pair),
}
}
par_chunks_mut!(ball_ball_prox, SIMD_WIDTH).for_each(|pairs| {
let context = ProximityDetectionContextSimd {
dispatcher: &DefaultProximityDispatcher,
prediction_distance,
colliders,
pairs,
};
context.pairs[0]
.detector
.detect_proximity_simd(context, events);
});
par_iter_mut!(shape_shape_prox).for_each(|pair| {
let context = ProximityDetectionContext {
dispatcher: &DefaultProximityDispatcher,
prediction_distance,
colliders,
pair,
};
context.pair.detector.detect_proximity(context, events);
});
ball_ball_prox.clear();
shape_shape_prox.clear();
*/
} }
pub(crate) fn compute_contacts( pub(crate) fn compute_contacts(
@@ -417,6 +359,11 @@ impl NarrowPhase {
return; return;
} }
if !co1.collision_groups.test(co2.collision_groups) {
// The collision is not allowed.
return;
}
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.
@@ -440,69 +387,6 @@ impl NarrowPhase {
.unwrap() .unwrap()
.generate_contacts(context, events); .generate_contacts(context, events);
}); });
/*
// First, group pairs.
// NOTE: the transmutes here are OK because the Vec are all cleared
// before we leave this method.
// We do this in order to avoid reallocating those vecs each time
// we compute the contacts. Unsafe is necessary because we can't just
// store a Vec<&mut ContactPair> into the NarrowPhase struct without
// polluting the World with lifetimes.
let ball_ball: &mut Vec<&mut ContactPair> =
unsafe { std::mem::transmute(&mut self.ball_ball) };
let shape_shape: &mut Vec<&mut ContactPair> =
unsafe { std::mem::transmute(&mut self.shape_shape) };
let bodies = &bodies.bodies;
// FIXME: don't iterate through all the interactions.
for pair in &mut self.contact_graph.interactions {
let co1 = &colliders[pair.pair.collider1];
let co2 = &colliders[pair.pair.collider2];
// FIXME: avoid lookup into bodies.
let rb1 = &bodies[co1.parent];
let rb2 = &bodies[co2.parent];
if (rb1.is_sleeping() || !rb1.is_dynamic()) && (rb2.is_sleeping() || !rb2.is_dynamic())
{
// No need to update this contact because nothing moved.
continue;
}
match (co1.shape(), co2.shape()) {
(Shape::Ball(_), Shape::Ball(_)) => ball_ball.push(pair),
_ => shape_shape.push(pair),
}
}
par_chunks_mut!(ball_ball, SIMD_WIDTH).for_each(|pairs| {
let context = ContactGenerationContextSimd {
dispatcher: &DefaultContactDispatcher,
prediction_distance,
colliders,
pairs,
};
context.pairs[0]
.generator
.generate_contacts_simd(context, events);
});
par_iter_mut!(shape_shape).for_each(|pair| {
let context = ContactGenerationContext {
dispatcher: &DefaultContactDispatcher,
prediction_distance,
colliders,
pair,
};
context.pair.generator.generate_contacts(context, events);
});
ball_ball.clear();
shape_shape.clear();
*/
} }
/// Retrieve all the interactions with at least one contact point, happening between two active bodies. /// Retrieve all the interactions with at least one contact point, happening between two active bodies.