1use crate::pipeline::broad_phase::BroadPhasePairFilter;
2use crate::pipeline::object::{CollisionObjectRef, CollisionObjectSet};
3use na::RealField;
45const SELF_COLLISION: u32 = 1 << 31;
6const ALL_GROUPS: u32 = (1 << 30) - 1;
7const NO_GROUP: u32 = 0;
89/// Groups of collision used to filter which object interact with which other one.
10///
11/// There are at most 30 groups indexed from 0 to 29 (included). This identifies collidable
12/// entities by combining three attributes:
13/// * A set of group this structure is member of.
14/// * A collision group whitelist.
15/// * A collision group blacklist.
16///
17/// For two entities to interact, they must be member of at least one group part of each-other's
18/// whitelists, and must not be part of any blacklisted group. The blacklist always has priority on
19/// the whitelist.
20///
21/// ### Example
22/// For example if the object A is such that:
23/// * It is part of the groups 1, 3, and 6.
24/// * It whitelists the groups 6 and 7.
25/// * It blacklists the group 1.
26///
27/// Let there be an object B such that:
28/// * It is part of the groups 1, 3, and 7.
29/// * It whitelists the groups 3 and 7.
30/// * It does not blacklist anything.
31///
32/// and an object C such that:
33/// * It is part of the groups 6 and 9.
34/// * It whitelists the groups 3 and 7.
35/// * It does not blacklist anything.
36///
37/// Then we have:
38/// * A and C can interact because A whitelists the group 6 (which C is part of), and,
39/// reciprocally, C whitelists the group 3 (which A is part of).
40/// * A and B will **not** interact because B is part of the group 1 which is blacklisted by A.
41/// * Finally, B and C will **not** interact either because, even if C whitelists the group 3
42/// (which B is part of), B does not whitelists the groups 6 nor 9 (which B is part of).
43#[derive(Clone, Debug, Copy)]
44pub struct CollisionGroups {
45 membership: u32,
46 whitelist: u32,
47 blacklist: u32,
48}
4950impl CollisionGroups {
51/// Creates a new `CollisionGroups` that enables interactions with everything except
52 /// self-interaction.
53#[inline]
54pub fn new() -> CollisionGroups {
55 CollisionGroups {
56 membership: ALL_GROUPS,
57 whitelist: ALL_GROUPS,
58 blacklist: NO_GROUP,
59 }
60 }
6162/// Creates a new `CollisionGroups` that disables interactions with everything.
63#[inline]
64pub fn empty() -> CollisionGroups {
65 CollisionGroups {
66 membership: NO_GROUP,
67 whitelist: NO_GROUP,
68 blacklist: NO_GROUP,
69 }
70 }
7172/// Returns a copy of this object, updated with a new set of membership groups.
73 ///
74 /// # Examples
75 ///
76 /// ```.ignore
77 /// const GROUP_A: usize = 0;
78 /// const GROUP_B: usize = 29;
79 /// let groups = CollisionGroups::new().with_membership(&[GROUP_A, GROUP_B]);
80 /// assert!(groups.is_member_of(GROUP_A));
81 /// assert!(groups.is_member_of(GROUP_B));
82 /// ```
83#[inline]
84pub fn with_membership(mut self, groups: &[usize]) -> CollisionGroups {
85 CollisionGroups::set_mask(&mut self.membership, groups);
86self
87}
8889/// Returns a copy of this object, updated with a new set of whitelisted groups.
90 ///
91 /// # Examples
92 ///
93 /// ```.ignore
94 /// const GROUP_A: usize = 0;
95 /// const GROUP_B: usize = 29;
96 /// let group_a = CollisionGroups::new().with_whitelist(&[GROUP_B]);
97 /// assert!(!group_a.is_group_whitelisted(GROUP_A));
98 /// assert!(group_a.is_group_whitelisted(GROUP_B));
99 /// ```
100#[inline]
101pub fn with_whitelist(mut self, groups: &[usize]) -> CollisionGroups {
102 CollisionGroups::set_mask(&mut self.whitelist, groups);
103self
104}
105106/// Returns a copy of this object, updated with a new set of blacklisted groups.
107 ///
108 /// # Examples
109 ///
110 /// ```.ignore
111 /// const GROUP_A: usize = 0;
112 /// const GROUP_B: usize = 29;
113 /// let group_a = CollisionGroups::new().with_blacklist(&[GROUP_B]);
114 /// assert!(!group_a.is_group_blacklisted(GROUP_A));
115 /// assert!(group_a.is_group_blacklisted(GROUP_B));
116 /// ```
117#[inline]
118pub fn with_blacklist(mut self, groups: &[usize]) -> CollisionGroups {
119 CollisionGroups::set_mask(&mut self.blacklist, groups);
120self
121}
122123/// The maximum allowed group identifier.
124#[inline]
125pub fn max_group_id() -> usize {
12629
127}
128129#[inline]
130fn modify_mask(mask: &mut u32, group_id: usize, add: bool) {
131assert!(
132 group_id < 30,
133"There are at most 30 groups indexed from 0 to 29 (included)."
134);
135136if add {
137*mask = *mask | (1 << group_id)
138 } else {
139*mask = *mask & !(1 << group_id)
140 }
141 }
142143#[inline]
144fn set_mask(mask: &mut u32, groups: &[usize]) {
145*mask = 0;
146for g in groups.iter() {
147 CollisionGroups::modify_mask(mask, *g, true);
148 }
149 }
150151#[inline]
152fn add_mask(cur_mask: u32, new_mask: u32) -> u32 {
153assert!(
154 new_mask <= ALL_GROUPS,
155"There are at most 30 groups indexed from 0 to 29 (included)."
156);
157158 cur_mask | new_mask
159 }
160161#[inline]
162fn remove_mask(cur_mask: u32, new_mask: u32) -> u32 {
163assert!(
164 new_mask <= ALL_GROUPS,
165"There are at most 30 groups indexed from 0 to 29 (included)."
166);
167168 cur_mask & !new_mask
169 }
170171#[inline]
172/// adds this entity to the given group by a mask of bits where each bit index represent a group
173pub fn add_membership_by_mask(mut self, group_mask: u32) -> CollisionGroups {
174self.membership = Self::add_mask(self.membership, group_mask);
175self
176}
177178#[inline]
179/// removes this entity from the given group by a mask of bits where each bit index represent a group
180pub fn remove_membership_by_mask(mut self, group_mask: u32) -> CollisionGroups {
181self.membership = Self::remove_mask(self.membership, group_mask);
182self
183}
184185#[inline]
186/// Replaces the membership with a mask of bits where each bit index represent a group
187pub fn with_membership_by_mask(mut self, group_mask: u32) -> CollisionGroups {
188assert!(
189 group_mask <= ALL_GROUPS,
190"There are at most 30 groups indexed from 0 to 29 (included)."
191);
192193self.membership = group_mask;
194self
195}
196197#[inline]
198/// adds this entity to this entity whitelist by a mask of bits where each bit index represent a group
199pub fn add_whitelist_by_mask(mut self, group_mask: u32) -> CollisionGroups {
200self.whitelist = Self::add_mask(self.whitelist, group_mask);
201self
202}
203204#[inline]
205/// remove this entity from this entity whitelist by a mask of bits where each bit index represent a group
206pub fn remove_whitelist_by_mask(mut self, group_mask: u32) -> CollisionGroups {
207self.whitelist = Self::remove_mask(self.whitelist, group_mask);
208self
209}
210211#[inline]
212/// Replaces the whitelist with a mask of bits where each bit index represent a group
213pub fn with_whitelist_by_mask(mut self, group_mask: u32) -> CollisionGroups {
214assert!(
215 group_mask <= ALL_GROUPS,
216"There are at most 30 groups indexed from 0 to 29 (included)."
217);
218219self.whitelist = group_mask;
220self
221}
222223#[inline]
224/// adds this entity to this entity blacklist by a mask of bits where each bit index represent a group
225pub fn add_blacklist_by_mask(mut self, group_mask: u32) -> CollisionGroups {
226self.blacklist = Self::add_mask(self.blacklist, group_mask);
227self
228}
229230#[inline]
231/// remove this entity from this entity blacklist by a mask of bits where each bit index represent a group
232pub fn remove_blacklist_by_mask(mut self, group_mask: u32) -> CollisionGroups {
233self.blacklist = Self::remove_mask(self.blacklist, group_mask);
234self
235}
236237#[inline]
238/// Replaces the blacklist with a mask of bits where each bit index represent a group
239pub fn with_blacklist_by_mask(mut self, group_mask: u32) -> CollisionGroups {
240assert!(
241 group_mask <= ALL_GROUPS,
242"There are at most 30 groups indexed from 0 to 29 (included)."
243);
244245self.blacklist = group_mask;
246self
247}
248249/// Adds or removes this entity from the given group.
250#[inline]
251pub fn modify_membership(&mut self, group_id: usize, add: bool) {
252 CollisionGroups::modify_mask(&mut self.membership, group_id, add);
253 }
254255/// Adds or removes the given group from this entity whitelist.
256#[inline]
257pub fn modify_whitelist(&mut self, group_id: usize, add: bool) {
258 CollisionGroups::modify_mask(&mut self.whitelist, group_id, add);
259 }
260261/// Adds or removes this entity from the given group.
262#[inline]
263pub fn modify_blacklist(&mut self, group_id: usize, add: bool) {
264 CollisionGroups::modify_mask(&mut self.blacklist, group_id, add);
265 }
266267/// Make this object member of the given groups only.
268#[inline]
269pub fn set_membership(&mut self, groups: &[usize]) {
270 CollisionGroups::set_mask(&mut self.membership, groups);
271 }
272273/// Whitelists the given groups only (others will be un-whitelisted).
274#[inline]
275pub fn set_whitelist(&mut self, groups: &[usize]) {
276 CollisionGroups::set_mask(&mut self.whitelist, groups);
277 }
278279/// Blacklists the given groups only (others will be un-blacklisted).
280#[inline]
281pub fn set_blacklist(&mut self, groups: &[usize]) {
282 CollisionGroups::set_mask(&mut self.blacklist, groups);
283 }
284285/// Copies the membership of another collision groups.
286#[inline]
287pub fn copy_membership(&mut self, other: &CollisionGroups) {
288self.membership = other.membership
289 }
290291/// Copies the whitelist of another collision groups.
292#[inline]
293pub fn copy_whitelist(&mut self, other: &CollisionGroups) {
294self.whitelist = other.whitelist
295 }
296297/// Copies the blacklist of another collision groups.
298#[inline]
299pub fn copy_blacklist(&mut self, other: &CollisionGroups) {
300self.blacklist = other.blacklist
301 }
302303/// Allows the object to interact with itself.
304#[inline]
305pub fn enable_self_interaction(&mut self) {
306self.whitelist = self.whitelist | SELF_COLLISION;
307 }
308309/// Prevents the object from interacting with itself.
310#[inline]
311pub fn disable_self_interaction(&mut self) {
312self.whitelist = self.whitelist & !SELF_COLLISION;
313 }
314315#[inline]
316fn is_inside_mask(mask: u32, group_id: usize) -> bool {
317assert!(
318 group_id < 30,
319"There are at most 30 groups indexed from 0 to 29 (included)."
320);
321 mask & (1 << group_id) != 0
322}
323324/// Tests if this entity is part of the given group.
325#[inline]
326pub fn is_member_of(&self, group_id: usize) -> bool {
327 CollisionGroups::is_inside_mask(self.membership, group_id)
328 }
329330/// Tests if the given group is whitelisted.
331#[inline]
332pub fn is_group_whitelisted(&self, group_id: usize) -> bool {
333 CollisionGroups::is_inside_mask(self.whitelist, group_id)
334 }
335336/// Tests if the given group is blacklisted.
337#[inline]
338pub fn is_group_blacklisted(&self, group_id: usize) -> bool {
339 CollisionGroups::is_inside_mask(self.blacklist, group_id)
340 }
341342/// Tests whether interactions with a given group is possible.
343 ///
344 /// Collision is possible if `group_id` is whitelisted but not blacklisted.
345#[inline]
346pub fn can_interact_with(&self, group_id: usize) -> bool {
347 !CollisionGroups::is_inside_mask(self.blacklist, group_id)
348 && CollisionGroups::is_inside_mask(self.whitelist, group_id)
349 }
350351/// Tests whether two collision groups have at least one group in common.
352#[inline]
353pub fn can_interact_with_groups(&self, other: &CollisionGroups) -> bool {
354// FIXME: is there a more bitwise-y way of doing this?
355self.membership & other.blacklist == 0
356&& other.membership & self.blacklist == 0
357&& self.membership & other.whitelist != 0
358&& other.membership & self.whitelist != 0
359}
360361/// Tests whether self-interaction is enabled.
362#[inline]
363pub fn can_interact_with_self(&self) -> bool {
364self.whitelist & SELF_COLLISION != 0
365}
366}
367368impl Default for CollisionGroups {
369#[inline]
370fn default() -> Self {
371 CollisionGroups::new()
372 }
373}
374375/// A collision filter based collision groups.
376pub struct CollisionGroupsPairFilter;
377378impl CollisionGroupsPairFilter {
379/// Creates a new collision filter based collision groups.
380#[inline]
381pub fn new() -> CollisionGroupsPairFilter {
382 CollisionGroupsPairFilter
383 }
384}
385386impl<N: RealField + Copy, Set: CollisionObjectSet<N>> BroadPhasePairFilter<N, Set>
387for CollisionGroupsPairFilter
388{
389fn is_pair_valid(
390&self,
391 h1: Set::CollisionObjectHandle,
392 h2: Set::CollisionObjectHandle,
393 s: &Set,
394 ) -> bool {
395let co1 = try_ret!(s.collision_object(h1), false);
396let co2 = try_ret!(s.collision_object(h2), false);
397398if h1 == h2 {
399 co1.collision_groups().can_interact_with_self()
400 } else {
401 co1.collision_groups()
402 .can_interact_with_groups(co2.collision_groups())
403 }
404 }
405}