66/// - **Memberships**: What groups does this collider belong to? (up to 32 groups)
77/// - **Filter**: What groups can this collider interact with?
88///
9- /// Two colliders interact only if:
10- /// 1. Collider A's memberships overlap with Collider B's filter, AND
11- /// 2. Collider B's memberships overlap with Collider A's filter
9+ /// An interaction is allowed between two colliders `a` and `b` when two conditions
10+ /// are met simultaneously for [`InteractionTestMode::And`] or individually for [`InteractionTestMode::Or`]::
11+ /// - The groups membership of `a` has at least one bit set to `1` in common with the groups filter of `b`.
12+ /// - The groups membership of `b` has at least one bit set to `1` in common with the groups filter of `a`.
1213///
14+ /// In other words, interactions are allowed between two colliders iff. the following condition is met
15+ /// for [`InteractionTestMode::And`]:
16+ /// ```ignore
17+ /// (self.memberships.bits() & rhs.filter.bits()) != 0 && (rhs.memberships.bits() & self.filter.bits()) != 0
18+ /// ```
19+ /// or for [`InteractionTestMode::Or`]:
20+ /// ```ignore
21+ /// (self.memberships.bits() & rhs.filter.bits()) != 0 || (rhs.memberships.bits() & self.filter.bits()) != 0
22+ /// ```
1323/// # Common use cases
1424///
1525/// - **Player vs. Enemy bullets**: Players in group 1, enemies in group 2. Player bullets
1828///
1929/// # Example
2030///
21- /// ```
31+ /// ```ignore
2232/// # use rapier3d::geometry::{InteractionGroups, Group};
2333/// // Player collider: in group 1, collides with groups 2 and 3
2434/// let player_groups = InteractionGroups::new(
25- /// Group::GROUP_1, // I am in group 1
26- /// Group::GROUP_2 | Group::GROUP_3 // I collide with groups 2 and 3
35+ /// Group::GROUP_1, // I am in group 1
36+ /// Group::GROUP_2, | Group::GROUP_3, // I collide with groups 2 and 3
37+ /// InteractionTestMode::And
2738/// );
2839///
2940/// // Enemy collider: in group 2, collides with group 1
3041/// let enemy_groups = InteractionGroups::new(
3142/// Group::GROUP_2, // I am in group 2
32- /// Group::GROUP_1 // I collide with group 1
43+ /// Group::GROUP_1, // I collide with group 1
44+ /// InteractionTestMode::And
3345/// );
3446///
3547/// // These will collide because:
@@ -45,29 +57,49 @@ pub struct InteractionGroups {
4557 pub memberships : Group ,
4658 /// Groups filter.
4759 pub filter : Group ,
60+ /// Interaction test mode
61+ ///
62+ /// In case of different test modes between two [`InteractionGroups`], [`InteractionTestMode::And`] is given priority.
63+ pub test_mode : InteractionTestMode ,
64+ }
65+
66+ #[ cfg_attr( feature = "serde-serialize" , derive( Serialize , Deserialize ) ) ]
67+ #[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq , Default ) ]
68+ /// Specifies which method should be used to test interactions.
69+ ///
70+ /// In case of different test modes between two [`InteractionGroups`], [`InteractionTestMode::And`] is given priority.
71+ pub enum InteractionTestMode {
72+ /// Use [`InteractionGroups::test_and`].
73+ #[ default]
74+ And ,
75+ /// Use [`InteractionGroups::test_or`], iff. the `rhs` is also [`InteractionTestMode::Or`].
76+ ///
77+ /// If the `rhs` is not [`InteractionTestMode::Or`], use [`InteractionGroups::test_and`].
78+ Or ,
4879}
4980
5081impl InteractionGroups {
5182 /// Initializes with the given interaction groups and interaction mask.
52- pub const fn new ( memberships : Group , filter : Group ) -> Self {
83+ pub const fn new ( memberships : Group , filter : Group , test_mode : InteractionTestMode ) -> Self {
5384 Self {
5485 memberships,
5586 filter,
87+ test_mode,
5688 }
5789 }
5890
5991 /// Creates a filter that allows interactions with everything (default behavior).
6092 ///
6193 /// The collider is in all groups and collides with all groups.
6294 pub const fn all ( ) -> Self {
63- Self :: new ( Group :: ALL , Group :: ALL )
95+ Self :: new ( Group :: ALL , Group :: ALL , InteractionTestMode :: And )
6496 }
6597
6698 /// Creates a filter that prevents all interactions.
6799 ///
68100 /// The collider won't collide with anything. Useful for temporarily disabled colliders.
69101 pub const fn none ( ) -> Self {
70- Self :: new ( Group :: NONE , Group :: NONE )
102+ Self :: new ( Group :: NONE , Group :: NONE , InteractionTestMode :: And )
71103 }
72104
73105 /// Sets the group this filter is part of.
@@ -85,21 +117,46 @@ impl InteractionGroups {
85117 /// Check if interactions should be allowed based on the interaction memberships and filter.
86118 ///
87119 /// An interaction is allowed iff. the memberships of `self` contain at least one bit set to 1 in common
88- /// with the filter of `rhs`, and vice-versa.
120+ /// with the filter of `rhs`, ** and** vice-versa.
89121 #[ inline]
90- pub const fn test ( self , rhs : Self ) -> bool {
122+ pub const fn test_and ( self , rhs : Self ) -> bool {
91123 // NOTE: since const ops is not stable, we have to convert `Group` into u32
92124 // to use & operator in const context.
93125 ( self . memberships . bits ( ) & rhs. filter . bits ( ) ) != 0
94126 && ( rhs. memberships . bits ( ) & self . filter . bits ( ) ) != 0
95127 }
128+
129+ /// Check if interactions should be allowed based on the interaction memberships and filter.
130+ ///
131+ /// An interaction is allowed iff. the groups of `self` contain at least one bit set to 1 in common
132+ /// with the mask of `rhs`, **or** vice-versa.
133+ #[ inline]
134+ pub const fn test_or ( self , rhs : Self ) -> bool {
135+ // NOTE: since const ops is not stable, we have to convert `Group` into u32
136+ // to use & operator in const context.
137+ ( self . memberships . bits ( ) & rhs. filter . bits ( ) ) != 0
138+ || ( rhs. memberships . bits ( ) & self . filter . bits ( ) ) != 0
139+ }
140+
141+ /// Check if interactions should be allowed based on the interaction memberships and filter.
142+ ///
143+ /// See [`InteractionTestMode`] for more info.
144+ #[ inline]
145+ pub const fn test ( self , rhs : Self ) -> bool {
146+ match ( self . test_mode , rhs. test_mode ) {
147+ ( InteractionTestMode :: And , _) => self . test_and ( rhs) ,
148+ ( InteractionTestMode :: Or , InteractionTestMode :: And ) => self . test_and ( rhs) ,
149+ ( InteractionTestMode :: Or , InteractionTestMode :: Or ) => self . test_or ( rhs) ,
150+ }
151+ }
96152}
97153
98154impl Default for InteractionGroups {
99155 fn default ( ) -> Self {
100156 Self {
101157 memberships : Group :: GROUP_1 ,
102158 filter : Group :: ALL ,
159+ test_mode : InteractionTestMode :: And ,
103160 }
104161 }
105162}
0 commit comments