Add collision groups to filter collision pairs.
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
54
src/geometry/interaction_groups.rs
Normal file
54
src/geometry/interaction_groups.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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")]
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user