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