Skip to main content

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}