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