diff --git a/src/geometry/interaction_groups.rs b/src/geometry/interaction_groups.rs index 37b7586d8..171fda4f1 100644 --- a/src/geometry/interaction_groups.rs +++ b/src/geometry/interaction_groups.rs @@ -3,30 +3,45 @@ #[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`). +/// This filtering method is based on two 32-bit values: +/// - The interaction groups (the 32 left-most bits of `self.0`). +/// - The interaction mask (the 32 right-most bits of `self.0`). /// /// An interaction is allowed between two filters `a` and `b` when two conditions -/// are met simultaneously: +/// are met simultaneously for [`Self::test_and`] or individually for [`Self::test_or`]: /// - 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: +/// In other words, interactions are allowed between two filter iff. the following condition is met +/// for [`Self::test_and`]: /// ```ignore -/// ((self.0 >> 16) & rhs.0) != 0 && ((rhs.0 >> 16) & self.0) != 0 +/// ((self.0 >> 32) & rhs.0) != 0 && ((rhs.0 >> 32) & self.0) != 0 /// ``` -pub struct InteractionGroups(pub u32); +/// or for [`Self::test_or`]: +/// ```ignore +/// ((self.0 >> 32) & rhs.0) != 0 || ((rhs.0 >> 32) & self.0) != 0 +/// ``` +pub struct InteractionGroups(pub u64); + +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +/// Specifies which method should be used to test interactions +pub enum InteractionTestMode { + /// Use [`InteractionGroups::test_and`]. + AND, + /// Use [`InteractionGroups::test_or`]. + OR, +} impl InteractionGroups { /// Initializes with the given interaction groups and interaction mask. - pub const fn new(groups: u16, masks: u16) -> Self { + pub const fn new(groups: u32, masks: u32) -> Self { Self::none().with_groups(groups).with_mask(masks) } /// Allow interaction with everything. pub const fn all() -> Self { - Self(u32::MAX) + Self(u64::MAX) } /// Prevent all interactions. @@ -35,22 +50,42 @@ impl InteractionGroups { } /// Sets the group this filter is part of. - pub const fn with_groups(self, groups: u16) -> Self { - Self((self.0 & 0x0000ffff) | ((groups as u32) << 16)) + pub const fn with_groups(self, groups: u32) -> Self { + Self((self.0 & 0x0000_0000_ffff_ffff) | ((groups as u64) << 32)) } /// Sets the interaction mask of this filter. - pub const fn with_mask(self, mask: u16) -> Self { - Self((self.0 & 0xffff0000) | (mask as u32)) + pub const fn with_mask(self, mask: u32) -> Self { + Self((self.0 & 0xffff_ffff_0000_0000) | (mask as u64)) } /// 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. + /// with the mask of `rhs`, **and** vice-versa. + #[inline] + pub const fn test_and(self, rhs: Self) -> bool { + ((self.0 >> 32) & rhs.0) != 0 && ((rhs.0 >> 32) & self.0) != 0 + } + + /// 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`, **or** vice-versa. + #[inline] + pub const fn test_or(self, rhs: Self) -> bool { + ((self.0 >> 32) & rhs.0) != 0 || ((rhs.0 >> 32) & self.0) != 0 + } + + /// Check if interactions should be allowed based on the interaction groups and mask. + /// + /// See [`InteractionTestMode`] for more info. #[inline] - pub const fn test(self, rhs: Self) -> bool { - ((self.0 >> 16) & rhs.0) != 0 && ((rhs.0 >> 16) & self.0) != 0 + pub const fn test(self, rhs: Self, mode: InteractionTestMode) -> bool { + match mode { + InteractionTestMode::AND => self.test_and(rhs), + InteractionTestMode::OR => self.test_or(rhs), + } } } diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index 1c83232b0..30631787b 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -8,7 +8,7 @@ pub use self::contact_pair::{ContactPair, SolverContact, SolverFlags}; pub use self::interaction_graph::{ ColliderGraphIndex, InteractionGraph, RigidBodyGraphIndex, TemporaryInteractionIndex, }; -pub use self::interaction_groups::InteractionGroups; +pub use self::interaction_groups::{InteractionGroups, InteractionTestMode}; pub use self::narrow_phase::NarrowPhase; pub use parry::query::TrackedContact; diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs index de199ec08..ce9e4c0ce 100644 --- a/src/geometry/narrow_phase.rs +++ b/src/geometry/narrow_phase.rs @@ -8,7 +8,7 @@ use crate::geometry::collider::ColliderChanges; use crate::geometry::{ BroadPhasePairEvent, ColliderGraphIndex, ColliderHandle, ColliderPair, ColliderSet, ContactData, ContactEvent, ContactManifold, ContactManifoldData, ContactPair, InteractionGraph, - IntersectionEvent, RemovedCollider, SolverContact, SolverFlags, + InteractionTestMode, IntersectionEvent, RemovedCollider, SolverContact, SolverFlags, }; use crate::math::{Real, Vector}; use crate::pipeline::{ @@ -55,6 +55,8 @@ pub struct NarrowPhase { intersection_graph: InteractionGraph, graph_indices: Coarena, removed_colliders: Option>, + collision_interaction_test_mode: InteractionTestMode, + solver_interaction_test_mode: InteractionTestMode, } pub(crate) type ContactManifoldIndex = usize; @@ -76,6 +78,8 @@ impl NarrowPhase { intersection_graph: InteractionGraph::new(), graph_indices: Coarena::new(), removed_colliders: None, + collision_interaction_test_mode: InteractionTestMode::AND, + solver_interaction_test_mode: InteractionTestMode::OR, } } @@ -502,6 +506,7 @@ impl NarrowPhase { let nodes = &self.intersection_graph.graph.nodes; let query_dispatcher = &*self.query_dispatcher; let active_hooks = hooks.active_hooks(); + let collision_interaction_test_mode = self.collision_interaction_test_mode; // TODO: don't iterate on all the edges. par_iter_mut!(&mut self.intersection_graph.graph.edges).for_each(|edge| { @@ -528,7 +533,10 @@ impl NarrowPhase { return; } - if !co1.collision_groups.test(co2.collision_groups) { + if !co1 + .collision_groups + .test(co2.collision_groups, collision_interaction_test_mode) + { // The intersection is not allowed. return; } @@ -588,6 +596,8 @@ impl NarrowPhase { let query_dispatcher = &*self.query_dispatcher; let active_hooks = hooks.active_hooks(); + let collision_interaction_test_mode = self.collision_interaction_test_mode; + let solver_interaction_test_mode = self.solver_interaction_test_mode; // TODO: don't iterate on all the edges. par_iter_mut!(&mut self.contact_graph.graph.edges).for_each(|edge| { @@ -613,7 +623,10 @@ impl NarrowPhase { return; } - if !co1.collision_groups.test(co2.collision_groups) { + if !co1 + .collision_groups + .test(co2.collision_groups, collision_interaction_test_mode) + { // The collision is not allowed. return; } @@ -647,7 +660,10 @@ impl NarrowPhase { co1.solver_flags | co2.solver_flags }; - if !co1.solver_groups.test(co2.solver_groups) { + if !co1 + .collision_groups + .test(co2.solver_groups, solver_interaction_test_mode) + { solver_flags.remove(SolverFlags::COMPUTE_IMPULSES); } @@ -808,4 +824,24 @@ impl NarrowPhase { } } } + + /// The test mode used to test the solver groups + pub fn solver_interaction_test_mode(&self) -> InteractionTestMode { + self.solver_interaction_test_mode + } + + /// The test mode used to test the collision groups + pub fn collision_interaction_test_mode(&self) -> InteractionTestMode { + self.collision_interaction_test_mode + } + + /// Sets the test mode used to test the solver groups + pub fn set_solver_interaction_test_mode(&mut self, mode: InteractionTestMode) { + self.solver_interaction_test_mode = mode; + } + + /// Sets the test mode used to test the collision groups + pub fn set_collision_interaction_test_mode(&mut self, mode: InteractionTestMode) { + self.collision_interaction_test_mode = mode; + } } diff --git a/src/pipeline/query_pipeline.rs b/src/pipeline/query_pipeline.rs index cb5614171..9483e2b0e 100644 --- a/src/pipeline/query_pipeline.rs +++ b/src/pipeline/query_pipeline.rs @@ -1,7 +1,7 @@ use crate::dynamics::RigidBodySet; use crate::geometry::{ - Collider, ColliderHandle, ColliderSet, InteractionGroups, PointProjection, Ray, - RayIntersection, SimdQuadTree, AABB, + Collider, ColliderHandle, ColliderSet, InteractionGroups, InteractionTestMode, PointProjection, + Ray, RayIntersection, SimdQuadTree, AABB, }; use crate::math::{Isometry, Point, Real, Vector}; use parry::query::details::{ @@ -30,6 +30,7 @@ pub struct QueryPipeline { quadtree: SimdQuadTree, tree_built: bool, dilation_factor: Real, + interaction_test_mode: InteractionTestMode, } struct QueryPipelineAsCompositeShape<'a> { @@ -65,7 +66,9 @@ impl<'a> TypedSimdCompositeShape for QueryPipelineAsCompositeShape<'a> { mut f: impl FnMut(Option<&Isometry>, &Self::PartShape), ) { if let Some(collider) = self.colliders.get(shape_id) { - if collider.collision_groups.test(self.query_groups) + if collider + .collision_groups + .test(self.query_groups, self.query_pipeline.interaction_test_mode) && self.filter.map(|f| f(shape_id, collider)).unwrap_or(true) { f(Some(collider.position()), collider.shape()) @@ -125,6 +128,7 @@ impl QueryPipeline { quadtree: SimdQuadTree::new(), tree_built: false, dilation_factor: 0.01, + interaction_test_mode: InteractionTestMode::AND, } } @@ -313,7 +317,9 @@ impl QueryPipeline { ) { let mut leaf_callback = &mut |handle: &ColliderHandle| { if let Some(coll) = colliders.get(*handle) { - if coll.collision_groups.test(query_groups) + if coll + .collision_groups + .test(query_groups, self.interaction_test_mode) && filter.map(|f| f(*handle, coll)).unwrap_or(true) { if let Some(hit) = @@ -418,7 +424,9 @@ impl QueryPipeline { ) { let mut leaf_callback = &mut |handle: &ColliderHandle| { if let Some(coll) = colliders.get(*handle) { - if coll.collision_groups.test(query_groups) + if coll + .collision_groups + .test(query_groups, self.interaction_test_mode) && filter.map(|f| f(*handle, coll)).unwrap_or(true) && coll.shape().contains_point(coll.position(), point) { @@ -588,7 +596,9 @@ impl QueryPipeline { let mut leaf_callback = &mut |handle: &ColliderHandle| { if let Some(coll) = colliders.get(*handle) { - if coll.collision_groups.test(query_groups) + if coll + .collision_groups + .test(query_groups, self.interaction_test_mode) && filter.map(|f| f(*handle, coll)).unwrap_or(true) { let pos12 = inv_shape_pos * coll.position(); @@ -607,4 +617,14 @@ impl QueryPipeline { self.quadtree.traverse_depth_first(&mut visitor); } + + /// The test mode used to test for intersection + pub fn interaction_test_mode(&self) -> InteractionTestMode { + self.interaction_test_mode + } + + /// Sets the test mode used to test for intersections + pub fn set_interaction_test_mode(&mut self, mode: InteractionTestMode) { + self.interaction_test_mode = mode; + } }