rapier2d/geometry/collider_components.rs
1use crate::alloc_prelude::*;
2use crate::dynamics::{CoefficientCombineRule, MassProperties, RigidBodyHandle, RigidBodyType};
3use crate::geometry::{InteractionGroups, Shape, SharedShape};
4use crate::math::{Pose, Real};
5use crate::pipeline::{ActiveEvents, ActiveHooks};
6use core::ops::{Deref, DerefMut};
7
8bitflags::bitflags! {
9 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
10 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
11 /// Flags describing how the collider has been modified by the user.
12 pub struct ColliderChanges: u32 {
13 /// Flag indicating that the collider handle is in the changed collider set.
14 const IN_MODIFIED_SET = 1 << 0;
15 /// Flag indicating that the density or mass-properties of this collider was changed.
16 const LOCAL_MASS_PROPERTIES = 1 << 1; // => RigidBody local mass-properties update.
17 /// Flag indicating that the `ColliderParent` component of the collider has been modified.
18 const PARENT = 1 << 2; // => BF & NF updates.
19 /// Flag indicating that the `ColliderPosition` component of the collider has been modified.
20 const POSITION = 1 << 3; // => BF & NF updates.
21 /// Flag indicating that the collision groups of the collider have been modified.
22 const GROUPS = 1 << 4; // => NF update.
23 /// Flag indicating that the `ColliderShape` component of the collider has been modified.
24 const SHAPE = 1 << 5; // => BF & NF update. NF pair workspace invalidation.
25 /// Flag indicating that the `ColliderType` component of the collider has been modified.
26 const TYPE = 1 << 6; // => NF update. NF pair invalidation.
27 /// Flag indicating that the dominance groups of the parent of this collider have been modified.
28 ///
29 /// This flags is automatically set by the `PhysicsPipeline` when the `RigidBodyChanges::DOMINANCE`
30 /// or `RigidBodyChanges::TYPE` of the parent rigid-body of this collider is detected.
31 const PARENT_EFFECTIVE_DOMINANCE = 1 << 7; // NF update.
32 /// Flag indicating that whether or not the collider is enabled was changed.
33 const ENABLED_OR_DISABLED = 1 << 8; // BF & NF updates.
34 }
35}
36
37impl Default for ColliderChanges {
38 fn default() -> Self {
39 ColliderChanges::empty()
40 }
41}
42
43impl ColliderChanges {
44 /// Do these changes justify a broad-phase update?
45 pub fn needs_broad_phase_update(self) -> bool {
46 self.intersects(
47 ColliderChanges::PARENT
48 | ColliderChanges::POSITION
49 | ColliderChanges::SHAPE
50 | ColliderChanges::ENABLED_OR_DISABLED,
51 )
52 }
53
54 /// Do these changes justify a narrow-phase update?
55 pub fn needs_narrow_phase_update(self) -> bool {
56 // NOTE: for simplicity of implementation, we return `true` even if
57 // we only need a dominance update. If this does become a
58 // bottleneck at some point in the future (which is very unlikely)
59 // we could do a special-case for dominance-only change (so that
60 // we only update the relative_dominance of the pre-existing contact.
61 self.bits() > 2
62 }
63}
64
65#[derive(Copy, Clone, Debug, PartialEq, Eq)]
66#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
67/// The type of collider.
68pub enum ColliderType {
69 /// A collider that can generate contacts and contact events.
70 Solid,
71 /// A collider that can generate intersection and intersection events.
72 Sensor,
73}
74
75impl ColliderType {
76 /// Is this collider a sensor?
77 pub fn is_sensor(self) -> bool {
78 self == ColliderType::Sensor
79 }
80}
81
82/// The shape of a collider.
83pub type ColliderShape = SharedShape;
84
85#[derive(Clone, PartialEq, Debug)]
86#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
87/// The mass-properties of a collider.
88pub enum ColliderMassProps {
89 /// The collider is given a density.
90 ///
91 /// Its actual `MassProperties` are computed automatically with
92 /// the help of [`Shape::mass_properties`].
93 Density(Real),
94 /// The collider is given a mass.
95 ///
96 /// Its angular inertia will be computed automatically based on this mass.
97 Mass(Real),
98 /// The collider is given explicit mass-properties.
99 MassProperties(Box<MassProperties>),
100}
101
102impl Default for ColliderMassProps {
103 fn default() -> Self {
104 ColliderMassProps::Density(1.0)
105 }
106}
107
108impl From<MassProperties> for ColliderMassProps {
109 fn from(mprops: MassProperties) -> Self {
110 ColliderMassProps::MassProperties(Box::new(mprops))
111 }
112}
113
114impl ColliderMassProps {
115 /// The mass-properties of this collider.
116 ///
117 /// If `self` is the `Density` variant, then this computes the mass-properties based
118 /// on the given shape.
119 ///
120 /// If `self` is the `MassProperties` variant, then this returns the stored mass-properties.
121 pub fn mass_properties(&self, shape: &dyn Shape) -> MassProperties {
122 match self {
123 ColliderMassProps::Density(density) => {
124 if *density != 0.0 {
125 shape.mass_properties(*density)
126 } else {
127 MassProperties::default()
128 }
129 }
130 ColliderMassProps::Mass(mass) => {
131 if *mass != 0.0 {
132 let mut mprops = shape.mass_properties(1.0);
133 mprops.set_mass(*mass, true);
134 mprops
135 } else {
136 MassProperties::default()
137 }
138 }
139 ColliderMassProps::MassProperties(mass_properties) => **mass_properties,
140 }
141 }
142}
143
144#[derive(Copy, Clone, Debug, PartialEq)]
145#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
146/// Information about the rigid-body this collider is attached to.
147pub struct ColliderParent {
148 /// Handle of the rigid-body this collider is attached to.
149 pub handle: RigidBodyHandle,
150 /// Const position of this collider relative to its parent rigid-body.
151 pub pos_wrt_parent: Pose,
152}
153
154#[derive(Copy, Clone, Debug, PartialEq)]
155#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
156/// The position of a collider.
157pub struct ColliderPosition(pub Pose);
158
159impl AsRef<Pose> for ColliderPosition {
160 #[inline]
161 fn as_ref(&self) -> &Pose {
162 &self.0
163 }
164}
165
166impl AsMut<Pose> for ColliderPosition {
167 fn as_mut(&mut self) -> &mut Pose {
168 &mut self.0
169 }
170}
171
172impl Deref for ColliderPosition {
173 type Target = Pose;
174 #[inline]
175 fn deref(&self) -> &Pose {
176 &self.0
177 }
178}
179
180impl DerefMut for ColliderPosition {
181 fn deref_mut(&mut self) -> &mut Self::Target {
182 &mut self.0
183 }
184}
185
186impl Default for ColliderPosition {
187 fn default() -> Self {
188 Self::identity()
189 }
190}
191
192impl ColliderPosition {
193 /// The identity position.
194 #[must_use]
195 pub fn identity() -> Self {
196 ColliderPosition(Pose::IDENTITY)
197 }
198}
199
200impl<T> From<T> for ColliderPosition
201where
202 Pose: From<T>,
203{
204 fn from(position: T) -> Self {
205 Self(position.into())
206 }
207}
208
209#[derive(Copy, Clone, Debug, PartialEq)]
210#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
211/// The constraints solver-related properties of this collider (friction, restitution, etc.)
212pub struct ColliderMaterial {
213 /// The friction coefficient of this collider.
214 ///
215 /// The greater the value, the stronger the friction forces will be.
216 /// Should be `>= 0`.
217 pub friction: Real,
218 /// The restitution coefficient of this collider.
219 ///
220 /// Increase this value to make contacts with this collider more "bouncy".
221 /// Should be `>= 0` and should generally not be greater than `1` (perfectly elastic
222 /// collision).
223 pub restitution: Real,
224 /// The rule applied to combine the friction coefficients of two colliders in contact.
225 pub friction_combine_rule: CoefficientCombineRule,
226 /// The rule applied to combine the restitution coefficients of two colliders.
227 pub restitution_combine_rule: CoefficientCombineRule,
228}
229
230impl ColliderMaterial {
231 /// Creates a new collider material with the given friction and restitution coefficients.
232 pub fn new(friction: Real, restitution: Real) -> Self {
233 Self {
234 friction,
235 restitution,
236 ..Default::default()
237 }
238 }
239}
240
241impl Default for ColliderMaterial {
242 fn default() -> Self {
243 Self {
244 friction: 1.0,
245 restitution: 0.0,
246 friction_combine_rule: CoefficientCombineRule::default(),
247 restitution_combine_rule: CoefficientCombineRule::default(),
248 }
249 }
250}
251
252bitflags::bitflags! {
253 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
254 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
255 /// Controls which combinations of body types can collide with each other.
256 ///
257 /// By default, Rapier only detects collisions between pairs that make physical sense
258 /// (e.g., dynamic-dynamic, dynamic-fixed). Use this to customize that behavior.
259 ///
260 /// **Most users don't need to change this** - the defaults are correct for normal physics.
261 ///
262 /// ## Default behavior
263 /// - ✅ Dynamic ↔ Dynamic (moving objects collide)
264 /// - ✅ Dynamic ↔ Fixed (moving objects hit walls)
265 /// - ✅ Dynamic ↔ Kinematic (moving objects hit platforms)
266 /// - ❌ Fixed ↔ Fixed (walls don't collide with each other - waste of CPU)
267 /// - ❌ Kinematic ↔ Kinematic (platforms don't collide - they're user-controlled)
268 /// - ❌ Kinematic ↔ Fixed (platforms don't collide with walls)
269 ///
270 /// # Example
271 /// ```
272 /// # use rapier3d::prelude::*;
273 /// # let mut colliders = ColliderSet::new();
274 /// # let mut bodies = RigidBodySet::new();
275 /// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
276 /// # let collider_handle = colliders.insert_with_parent(ColliderBuilder::ball(0.5), body_handle, &mut bodies);
277 /// # let collider = colliders.get_mut(collider_handle).unwrap();
278 /// // Enable kinematic-kinematic collisions (unusual)
279 /// let types = ActiveCollisionTypes::default() | ActiveCollisionTypes::KINEMATIC_KINEMATIC;
280 /// collider.set_active_collision_types(types);
281 /// ```
282 pub struct ActiveCollisionTypes: u16 {
283 /// Enables dynamic ↔ dynamic collision detection.
284 const DYNAMIC_DYNAMIC = 0b0000_0000_0000_0001;
285 /// Enables dynamic ↔ kinematic collision detection.
286 const DYNAMIC_KINEMATIC = 0b0000_0000_0000_1100;
287 /// Enables dynamic ↔ fixed collision detection.
288 const DYNAMIC_FIXED = 0b0000_0000_0000_0010;
289 /// Enables kinematic ↔ kinematic collision detection (rarely needed).
290 const KINEMATIC_KINEMATIC = 0b1100_1100_0000_0000;
291 /// Enables kinematic ↔ fixed collision detection (rarely needed).
292 const KINEMATIC_FIXED = 0b0010_0010_0000_0000;
293 /// Enables fixed ↔ fixed collision detection (rarely needed).
294 const FIXED_FIXED = 0b0000_0000_0010_0000;
295 }
296}
297
298impl ActiveCollisionTypes {
299 /// Test whether contact should be computed between two rigid-bodies with the given types.
300 pub fn test(self, rb_type1: RigidBodyType, rb_type2: RigidBodyType) -> bool {
301 // NOTE: This test is quite complicated so here is an explanation.
302 // First, we associate the following bit masks:
303 // - DYNAMIC = 0001
304 // - FIXED = 0010
305 // - KINEMATIC = 1100
306 // These are equal to the bits indexed by `RigidBodyType as u32`.
307 // The bit masks defined by ActiveCollisionTypes are defined is such a way
308 // that the first part of the variant name (e.g. DYNAMIC_*) indicates which
309 // groups of four bits should be considered:
310 // - DYNAMIC_* = the first group of four bits.
311 // - FIXED_* = the second group of four bits.
312 // - KINEMATIC_* = the third and fourth groups of four bits.
313 // The second part of the variant name (e.g. *_DYNAMIC) indicates the value
314 // of the aforementioned groups of four bits.
315 // For example, DYNAMIC_FIXED means that the first group of four bits (because
316 // of DYNAMIC_*) must have the value 0010 (because of *_FIXED). That gives
317 // us 0b0000_0000_0000_0010 for the DYNAMIC_FIXED_VARIANT.
318 //
319 // The KINEMATIC_* is special because it occupies two groups of four bits. This is
320 // because it combines both KinematicPositionBased and KinematicVelocityBased.
321 //
322 // Now that we have a way of building these bit masks, let's see how we use them.
323 // Given a pair of rigid-body types, the first rigid-body type is used to select
324 // the group of four bits we want to test (the selection is done by to the
325 // `>> (rb_type1 as u32 * 4) & 0b0000_1111`) and the second rigid-body type is
326 // used to form the bit mask we test this group of four bits against.
327 // In other word, the selection of the group of four bits tells us "for this type
328 // of rigid-body I can have collision with rigid-body types with these bit representation".
329 // Then the `(1 << rb_type2)` gives us the bit-representation of the rigid-body type,
330 // which needs to be checked.
331 //
332 // Because that test must be symmetric, we perform two similar tests by swapping
333 // rb_type1 and rb_type2.
334 ((self.bits() >> (rb_type1 as u32 * 4)) & 0b0000_1111) & (1 << rb_type2 as u32) != 0
335 || ((self.bits() >> (rb_type2 as u32 * 4)) & 0b0000_1111) & (1 << rb_type1 as u32) != 0
336 }
337}
338
339impl Default for ActiveCollisionTypes {
340 fn default() -> Self {
341 ActiveCollisionTypes::DYNAMIC_DYNAMIC
342 | ActiveCollisionTypes::DYNAMIC_KINEMATIC
343 | ActiveCollisionTypes::DYNAMIC_FIXED
344 }
345}
346
347#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
348#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
349/// Enum indicating whether or not a collider is enabled.
350pub enum ColliderEnabled {
351 /// The collider is enabled.
352 Enabled,
353 /// The collider wasn’t disabled by the user explicitly but it is attached to
354 /// a disabled rigid-body.
355 DisabledByParent,
356 /// The collider is disabled by the user explicitly.
357 Disabled,
358}
359
360#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
361#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
362/// A set of flags for controlling collision/intersection filtering, modification, and events.
363pub struct ColliderFlags {
364 /// Controls whether collision-detection happens between two colliders depending on
365 /// the type of the rigid-bodies they are attached to.
366 pub active_collision_types: ActiveCollisionTypes,
367 /// The groups controlling the pairs of colliders that can interact (generate
368 /// interaction events or contacts).
369 pub collision_groups: InteractionGroups,
370 /// The groups controlling the pairs of collider that have their contact
371 /// points taken into account for force computation.
372 pub solver_groups: InteractionGroups,
373 /// The physics hooks enabled for contact pairs and intersection pairs involving this collider.
374 pub active_hooks: ActiveHooks,
375 /// The events enabled for this collider.
376 pub active_events: ActiveEvents,
377 /// Whether or not the collider is enabled.
378 pub enabled: ColliderEnabled,
379}
380
381impl Default for ColliderFlags {
382 fn default() -> Self {
383 Self {
384 active_collision_types: ActiveCollisionTypes::default(),
385 collision_groups: InteractionGroups::all(),
386 solver_groups: InteractionGroups::all(),
387 active_hooks: ActiveHooks::empty(),
388 active_events: ActiveEvents::empty(),
389 enabled: ColliderEnabled::Enabled,
390 }
391 }
392}
393
394impl From<ActiveHooks> for ColliderFlags {
395 fn from(active_hooks: ActiveHooks) -> Self {
396 Self {
397 active_hooks,
398 ..Default::default()
399 }
400 }
401}
402
403impl From<ActiveEvents> for ColliderFlags {
404 fn from(active_events: ActiveEvents) -> Self {
405 Self {
406 active_events,
407 ..Default::default()
408 }
409 }
410}