diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/geometry/collider.rs | 24 | ||||
| -rw-r--r-- | src/geometry/interaction_groups.rs | 54 | ||||
| -rw-r--r-- | src/geometry/mod.rs | 2 | ||||
| -rw-r--r-- | src/geometry/narrow_phase.rs | 136 |
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. |
