rapier3d/geometry/
interaction_groups.rs

1#![allow(clippy::bad_bit_mask)] // Clippy will complain about the bitmasks due to Group::NONE being 0.
2
3/// Collision filtering system that controls which colliders can interact with each other.
4///
5/// Think of this as "collision layers" in game engines. Each collider has:
6/// - **Memberships**: What groups does this collider belong to? (up to 32 groups)
7/// - **Filter**: What groups can this collider interact with?
8///
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
12///
13/// # Common use cases
14///
15/// - **Player vs. Enemy bullets**: Players in group 1, enemies in group 2. Player bullets
16///   only hit group 2, enemy bullets only hit group 1.
17/// - **Trigger zones**: Sensors that only detect specific object types.
18///
19/// # Example
20///
21/// ```
22/// # use rapier3d::geometry::{InteractionGroups, Group};
23/// // Player collider: in group 1, collides with groups 2 and 3
24/// 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
27/// );
28///
29/// // Enemy collider: in group 2, collides with group 1
30/// let enemy_groups = InteractionGroups::new(
31///     Group::GROUP_2,  // I am in group 2
32///     Group::GROUP_1   // I collide with group 1
33/// );
34///
35/// // These will collide because:
36/// // - Player's membership (GROUP_1) is in enemy's filter (GROUP_1) ✓
37/// // - Enemy's membership (GROUP_2) is in player's filter (GROUP_2) ✓
38/// assert!(player_groups.test(enemy_groups));
39/// ```
40#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
41#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
42#[repr(C)]
43pub struct InteractionGroups {
44    /// Groups memberships.
45    pub memberships: Group,
46    /// Groups filter.
47    pub filter: Group,
48}
49
50impl InteractionGroups {
51    /// Initializes with the given interaction groups and interaction mask.
52    pub const fn new(memberships: Group, filter: Group) -> Self {
53        Self {
54            memberships,
55            filter,
56        }
57    }
58
59    /// Creates a filter that allows interactions with everything (default behavior).
60    ///
61    /// The collider is in all groups and collides with all groups.
62    pub const fn all() -> Self {
63        Self::new(Group::ALL, Group::ALL)
64    }
65
66    /// Creates a filter that prevents all interactions.
67    ///
68    /// The collider won't collide with anything. Useful for temporarily disabled colliders.
69    pub const fn none() -> Self {
70        Self::new(Group::NONE, Group::NONE)
71    }
72
73    /// Sets the group this filter is part of.
74    pub const fn with_memberships(mut self, memberships: Group) -> Self {
75        self.memberships = memberships;
76        self
77    }
78
79    /// Sets the interaction mask of this filter.
80    pub const fn with_filter(mut self, filter: Group) -> Self {
81        self.filter = filter;
82        self
83    }
84
85    /// Check if interactions should be allowed based on the interaction memberships and filter.
86    ///
87    /// 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.
89    #[inline]
90    pub const fn test(self, rhs: Self) -> bool {
91        // NOTE: since const ops is not stable, we have to convert `Group` into u32
92        // to use & operator in const context.
93        (self.memberships.bits() & rhs.filter.bits()) != 0
94            && (rhs.memberships.bits() & self.filter.bits()) != 0
95    }
96}
97
98impl Default for InteractionGroups {
99    fn default() -> Self {
100        Self {
101            memberships: Group::GROUP_1,
102            filter: Group::ALL,
103        }
104    }
105}
106
107bitflags::bitflags! {
108    /// A bit mask identifying groups for interaction.
109    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
110    #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
111    pub struct Group: u32 {
112        /// The group n°1.
113        const GROUP_1 = 1 << 0;
114        /// The group n°2.
115        const GROUP_2 = 1 << 1;
116        /// The group n°3.
117        const GROUP_3 = 1 << 2;
118        /// The group n°4.
119        const GROUP_4 = 1 << 3;
120        /// The group n°5.
121        const GROUP_5 = 1 << 4;
122        /// The group n°6.
123        const GROUP_6 = 1 << 5;
124        /// The group n°7.
125        const GROUP_7 = 1 << 6;
126        /// The group n°8.
127        const GROUP_8 = 1 << 7;
128        /// The group n°9.
129        const GROUP_9 = 1 << 8;
130        /// The group n°10.
131        const GROUP_10 = 1 << 9;
132        /// The group n°11.
133        const GROUP_11 = 1 << 10;
134        /// The group n°12.
135        const GROUP_12 = 1 << 11;
136        /// The group n°13.
137        const GROUP_13 = 1 << 12;
138        /// The group n°14.
139        const GROUP_14 = 1 << 13;
140        /// The group n°15.
141        const GROUP_15 = 1 << 14;
142        /// The group n°16.
143        const GROUP_16 = 1 << 15;
144        /// The group n°17.
145        const GROUP_17 = 1 << 16;
146        /// The group n°18.
147        const GROUP_18 = 1 << 17;
148        /// The group n°19.
149        const GROUP_19 = 1 << 18;
150        /// The group n°20.
151        const GROUP_20 = 1 << 19;
152        /// The group n°21.
153        const GROUP_21 = 1 << 20;
154        /// The group n°22.
155        const GROUP_22 = 1 << 21;
156        /// The group n°23.
157        const GROUP_23 = 1 << 22;
158        /// The group n°24.
159        const GROUP_24 = 1 << 23;
160        /// The group n°25.
161        const GROUP_25 = 1 << 24;
162        /// The group n°26.
163        const GROUP_26 = 1 << 25;
164        /// The group n°27.
165        const GROUP_27 = 1 << 26;
166        /// The group n°28.
167        const GROUP_28 = 1 << 27;
168        /// The group n°29.
169        const GROUP_29 = 1 << 28;
170        /// The group n°30.
171        const GROUP_30 = 1 << 29;
172        /// The group n°31.
173        const GROUP_31 = 1 << 30;
174        /// The group n°32.
175        const GROUP_32 = 1 << 31;
176
177        /// All of the groups.
178        const ALL = u32::MAX;
179        /// None of the groups.
180        const NONE = 0;
181    }
182}
183
184impl From<u32> for Group {
185    #[inline]
186    fn from(val: u32) -> Self {
187        Self::from_bits_retain(val)
188    }
189}
190
191impl From<Group> for u32 {
192    #[inline]
193    fn from(val: Group) -> Self {
194        val.bits()
195    }
196}