aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/geometry/collider.rs24
-rw-r--r--src/geometry/interaction_groups.rs54
-rw-r--r--src/geometry/mod.rs2
-rw-r--r--src/geometry/narrow_phase.rs136
4 files changed, 87 insertions, 129 deletions
diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs
index 40b59ae..7147e95 100644
--- a/src/geometry/collider.rs
+++ b/src/geometry/collider.rs
@@ -1,7 +1,7 @@
use crate::dynamics::{MassProperties, RigidBodyHandle, RigidBodySet};
use crate::geometry::{
- Ball, Capsule, ColliderGraphIndex, Contact, Cuboid, HeightField, InteractionGraph, Proximity,
- Segment, Shape, ShapeType, Triangle, Trimesh,
+ Ball, Capsule, ColliderGraphIndex, Contact, Cuboid, HeightField, InteractionGraph,
+ InteractionGroups, Proximity, Segment, Shape, ShapeType, Triangle, Trimesh,
};
#[cfg(feature = "dim3")]
use crate::geometry::{Cone, Cylinder, RoundCylinder};
@@ -203,6 +203,7 @@ pub struct Collider {
pub friction: f32,
/// The restitution coefficient of this collider.
pub restitution: f32,
+ pub(crate) collision_groups: InteractionGroups,
pub(crate) contact_graph_index: ColliderGraphIndex,
pub(crate) proximity_graph_index: ColliderGraphIndex,
pub(crate) proxy_index: usize,
@@ -255,6 +256,11 @@ impl Collider {
&self.delta
}
+ /// The collision groups used by this collider.
+ pub fn collision_groups(&self) -> InteractionGroups {
+ self.collision_groups
+ }
+
/// The density of this collider.
pub fn density(&self) -> f32 {
self.density
@@ -300,6 +306,8 @@ pub struct ColliderBuilder {
pub is_sensor: bool,
/// The user-data of the collider beind built.
pub user_data: u128,
+ /// The collision groups for the collider being built.
+ pub collision_groups: InteractionGroups,
}
impl ColliderBuilder {
@@ -313,6 +321,7 @@ impl ColliderBuilder {
delta: Isometry::identity(),
is_sensor: false,
user_data: 0,
+ collision_groups: InteractionGroups::all(),
}
}
@@ -418,12 +427,21 @@ impl ColliderBuilder {
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 {
self.user_data = data;
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.
pub fn sensor(mut self, is_sensor: bool) -> Self {
self.is_sensor = is_sensor;
diff --git a/src/geometry/interaction_groups.rs b/src/geometry/interaction_groups.rs
new file mode 100644
index 0000000..9596345
--- /dev/null
+++ b/src/geometry/interaction_groups.rs
@@ -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()
+ }
+}
diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs
index abb9844..4055d43 100644
--- a/src/geometry/mod.rs
+++ b/src/geometry/mod.rs
@@ -70,6 +70,7 @@ pub(crate) use self::polyhedron_feature3d::PolyhedronFace;
pub(crate) use self::waabb::{WRay, WAABB};
pub(crate) use self::wquadtree::WQuadtree;
//pub(crate) use self::z_order::z_cmp_floats;
+pub use self::interaction_groups::InteractionGroups;
pub use self::shape::{Shape, ShapeType};
mod ball;
@@ -97,6 +98,7 @@ mod waabb;
mod wquadtree;
//mod z_order;
mod capsule;
+mod interaction_groups;
#[cfg(feature = "dim3")]
mod polygonal_feature_map;
#[cfg(feature = "dim3")]
diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs
index e95709c..5bcdcdb 100644
--- a/src/geometry/narrow_phase.rs
+++ b/src/geometry/narrow_phase.rs
@@ -306,6 +306,11 @@ impl NarrowPhase {
return;
}
+ if !co1.collision_groups.test(co2.collision_groups) {
+ // The collision is not allowed.
+ return;
+ }
+
let dispatcher = DefaultProximityDispatcher;
if pair.detector.is_none() {
// We need a redispatch for this detector.
@@ -329,69 +334,6 @@ impl NarrowPhase {
.unwrap()
.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(
@@ -417,6 +359,11 @@ impl NarrowPhase {
return;
}
+ if !co1.collision_groups.test(co2.collision_groups) {
+ // The collision is not allowed.
+ return;
+ }
+
let dispatcher = DefaultContactDispatcher;
if pair.generator.is_none() {
// We need a redispatch for this generator.
@@ -440,69 +387,6 @@ impl NarrowPhase {
.unwrap()
.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.