bevy_rapier2d/geometry/
collider.rs

1use std::fmt;
2
3#[cfg(all(feature = "dim3", feature = "async-collider"))]
4use {
5    crate::geometry::{TriMeshFlags, VHACDParameters},
6    bevy::platform::collections::HashMap,
7};
8
9use bevy::prelude::*;
10
11use bevy::platform::collections::HashSet;
12use rapier::geometry::Shape;
13use rapier::prelude::{ColliderHandle, InteractionGroups, SharedShape};
14
15use crate::dynamics::{CoefficientCombineRule, MassProperties};
16use crate::math::Vect;
17
18#[cfg(doc)]
19use rapier::{dynamics::RigidBody, geometry::ContactForceEvent};
20
21/// The Rapier handle of a collider that was inserted to the physics scene.
22#[derive(Copy, Clone, Debug, Component)]
23pub struct RapierColliderHandle(pub ColliderHandle);
24
25/// A component which will be replaced by the specified collider type after the referenced mesh become available.
26#[cfg(all(feature = "dim3", feature = "async-collider"))]
27#[derive(Component, Debug, Clone, Default)]
28pub struct AsyncCollider(pub ComputedColliderShape);
29
30/// A component which will be replaced the specified collider types on children with meshes after the referenced scene become available.
31#[cfg(all(feature = "dim3", feature = "async-collider"))]
32#[derive(Component, Debug, Clone)]
33pub struct AsyncSceneCollider {
34    /// Collider type for each scene mesh not included in [`Self::named_shapes`]. If [`None`], then all
35    /// shapes will be skipped for processing except [`Self::named_shapes`].
36    pub shape: Option<ComputedColliderShape>,
37    /// Shape types for meshes by name. If shape is [`None`], then it will be skipped for
38    /// processing.
39    pub named_shapes: HashMap<String, Option<ComputedColliderShape>>,
40}
41
42#[cfg(all(feature = "dim3", feature = "async-collider"))]
43impl Default for AsyncSceneCollider {
44    fn default() -> Self {
45        Self {
46            shape: Some(Default::default()),
47            named_shapes: Default::default(),
48        }
49    }
50}
51
52/// Shape type based on a Bevy mesh asset.
53#[cfg(all(feature = "dim3", feature = "async-collider"))]
54#[derive(Debug, Clone)]
55pub enum ComputedColliderShape {
56    /// Triangle-mesh.
57    TriMesh(TriMeshFlags),
58    /// Convex hull.
59    ConvexHull,
60    /// Convex decomposition.
61    ConvexDecomposition(VHACDParameters),
62}
63
64#[cfg(all(feature = "dim3", feature = "async-collider"))]
65impl Default for ComputedColliderShape {
66    fn default() -> Self {
67        Self::TriMesh(TriMeshFlags::MERGE_DUPLICATE_VERTICES)
68    }
69}
70
71/// A geometric entity that can be attached to a [`RigidBody`] so it can be affected by contacts
72/// and intersection queries.
73///
74/// Related components:
75/// - [`ColliderMassProperties`]
76/// - [`Friction`]
77/// - [`Restitution`]
78/// - [`Sensor`]
79/// - [`CollisionGroups`]
80/// - [`SolverGroups`]
81/// - [`ActiveCollisionTypes`]
82/// - [`ActiveEvents`]
83/// - [`ContactForceEventThreshold`]
84/// - [`CollidingEntities`]
85/// - [`ColliderScale`]
86/// - [`ColliderDisabled`]
87#[derive(Component, Clone)] // TODO: Reflect
88#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
89pub struct Collider {
90    /// The raw shape from Rapier.
91    pub raw: SharedShape,
92    pub(crate) unscaled: SharedShape,
93    pub(crate) scale: Vect,
94}
95
96impl From<SharedShape> for Collider {
97    fn from(shared_shape: SharedShape) -> Collider {
98        Collider {
99            raw: shared_shape.clone(),
100            unscaled: shared_shape,
101            scale: Vect::ONE,
102        }
103    }
104}
105
106impl<'a> From<&'a Collider> for &'a dyn Shape {
107    fn from(collider: &'a Collider) -> &'a dyn Shape {
108        &*collider.raw
109    }
110}
111
112impl fmt::Debug for Collider {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        self.as_typed_shape().fmt(f)
115    }
116}
117
118/// Overwrites the default application of [`GlobalTransform`] scale to a [`Collider`]'s shapes.
119#[derive(Copy, Clone, Debug, PartialEq, Component, Reflect)]
120pub enum ColliderScale {
121    /// This scale will be multiplied with the scale in the [`GlobalTransform`] component
122    /// before being applied to the collider.
123    Relative(Vect),
124    /// This scale will replace the one specified in the [`GlobalTransform`] component.
125    Absolute(Vect),
126}
127
128/// Indicates whether or not the [`Collider`] is a sensor.
129#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Component, Reflect)]
130#[reflect(Component, Default, PartialEq)]
131pub struct Sensor;
132
133/// Custom mass-properties of a [`Collider`].
134#[derive(Copy, Clone, Debug, PartialEq, Component, Reflect)]
135#[reflect(Component, Default, PartialEq)]
136pub enum ColliderMassProperties {
137    /// The mass-properties are computed automatically from the collider’s shape and this density.
138    Density(f32),
139    /// The mass-properties are computed automatically from the collider’s shape and this mass.
140    Mass(f32),
141    /// The mass-properties of the collider are replaced by the ones specified here.
142    MassProperties(MassProperties),
143}
144
145impl Default for ColliderMassProperties {
146    fn default() -> Self {
147        Self::Density(1.0)
148    }
149}
150
151/// The friction affecting a [`Collider`].
152#[derive(Copy, Clone, Debug, PartialEq, Component, Reflect)]
153#[reflect(Component, Default, PartialEq)]
154pub struct Friction {
155    /// The friction coefficient of a collider.
156    ///
157    /// The greater the value, the stronger the friction forces will be.
158    /// Should be `>= 0`.
159    pub coefficient: f32,
160    /// The rule applied to combine the friction coefficients of two colliders in contact.
161    pub combine_rule: CoefficientCombineRule,
162}
163
164impl Default for Friction {
165    fn default() -> Self {
166        Self {
167            coefficient: 0.5,
168            combine_rule: CoefficientCombineRule::Average,
169        }
170    }
171}
172
173impl Friction {
174    /// Creates a [`Friction`] component from the given friction coefficient, and using the default
175    /// [`CoefficientCombineRule::Average`] coefficient combine rule.
176    pub const fn new(coefficient: f32) -> Self {
177        Self {
178            coefficient,
179            combine_rule: CoefficientCombineRule::Average,
180        }
181    }
182
183    /// Creates a [`Friction`] component from the given friction coefficient, and using the default
184    /// [`CoefficientCombineRule::Average`] coefficient combine rule.
185    pub const fn coefficient(coefficient: f32) -> Self {
186        Self {
187            coefficient,
188            combine_rule: CoefficientCombineRule::Average,
189        }
190    }
191}
192
193/// The restitution affecting a [`Collider`].
194#[derive(Copy, Clone, Debug, PartialEq, Component, Reflect)]
195#[reflect(Component, Default, PartialEq)]
196pub struct Restitution {
197    /// The restitution coefficient of a collider.
198    ///
199    /// The greater the value, the stronger the restitution forces will be.
200    /// Should be `>= 0`.
201    pub coefficient: f32,
202    /// The rule applied to combine the friction coefficients of two colliders in contact.
203    pub combine_rule: CoefficientCombineRule,
204}
205
206impl Restitution {
207    /// Creates a [`Restitution`] component from the given restitution coefficient, and using the default
208    /// [`CoefficientCombineRule::Average`] coefficient combine rule.
209    pub const fn new(coefficient: f32) -> Self {
210        Self {
211            coefficient,
212            combine_rule: CoefficientCombineRule::Average,
213        }
214    }
215
216    /// Creates a [`Restitution`] component from the given restitution coefficient, and using the default
217    /// [`CoefficientCombineRule::Average`] coefficient combine rule.
218    pub const fn coefficient(coefficient: f32) -> Self {
219        Self {
220            coefficient,
221            combine_rule: CoefficientCombineRule::Average,
222        }
223    }
224}
225
226impl Default for Restitution {
227    fn default() -> Self {
228        Self {
229            coefficient: 0.0,
230            combine_rule: CoefficientCombineRule::Average,
231        }
232    }
233}
234
235#[derive(Component, Reflect, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
236#[reflect(Component, Default, Hash, PartialEq)]
237#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
238/// Flags affecting whether or not collision-detection happens between two colliders
239/// depending on the type of rigid-bodies they are attached to.
240pub struct ActiveCollisionTypes(u16);
241
242bitflags::bitflags! {
243    impl ActiveCollisionTypes: u16 {
244        /// Enable collision-detection between a collider attached to a dynamic body
245        /// and another collider attached to a dynamic body.
246        const DYNAMIC_DYNAMIC = 0b0000_0000_0000_0001;
247        /// Enable collision-detection between a collider attached to a dynamic body
248        /// and another collider attached to a kinematic body.
249        const DYNAMIC_KINEMATIC = 0b0000_0000_0000_1100;
250        /// Enable collision-detection between a collider attached to a dynamic body
251        /// and another collider attached to a fixed body (or not attached to any body).
252        const DYNAMIC_STATIC  = 0b0000_0000_0000_0010;
253        /// Enable collision-detection between a collider attached to a kinematic body
254        /// and another collider attached to a kinematic body.
255        const KINEMATIC_KINEMATIC = 0b1100_1100_0000_0000;
256
257        /// Enable collision-detection between a collider attached to a kinematic body
258        /// and another collider attached to a fixed body (or not attached to any body).
259        const KINEMATIC_STATIC = 0b0010_0010_0000_0000;
260
261        /// Enable collision-detection between a collider attached to a fixed body (or
262        /// not attached to any body) and another collider attached to a fixed body (or
263        /// not attached to any body).
264        const STATIC_STATIC = 0b0000_0000_0010_0000;
265    }
266}
267
268impl Default for ActiveCollisionTypes {
269    fn default() -> Self {
270        Self::DYNAMIC_DYNAMIC | Self::DYNAMIC_KINEMATIC | Self::DYNAMIC_STATIC
271    }
272}
273
274impl From<ActiveCollisionTypes> for rapier::geometry::ActiveCollisionTypes {
275    fn from(collision_types: ActiveCollisionTypes) -> rapier::geometry::ActiveCollisionTypes {
276        rapier::geometry::ActiveCollisionTypes::from_bits(collision_types.bits())
277            .expect("Internal error: invalid active events conversion.")
278    }
279}
280
281/// A bit mask identifying groups for interaction.
282#[derive(Component, Reflect, Copy, Clone, Debug, PartialEq, Eq, Hash)]
283#[reflect(Component, Default, Hash, PartialEq)]
284#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
285pub struct Group(u32);
286
287bitflags::bitflags! {
288    impl Group: u32 {
289        /// The group n°1.
290        const GROUP_1 = 1 << 0;
291        /// The group n°2.
292        const GROUP_2 = 1 << 1;
293        /// The group n°3.
294        const GROUP_3 = 1 << 2;
295        /// The group n°4.
296        const GROUP_4 = 1 << 3;
297        /// The group n°5.
298        const GROUP_5 = 1 << 4;
299        /// The group n°6.
300        const GROUP_6 = 1 << 5;
301        /// The group n°7.
302        const GROUP_7 = 1 << 6;
303        /// The group n°8.
304        const GROUP_8 = 1 << 7;
305        /// The group n°9.
306        const GROUP_9 = 1 << 8;
307        /// The group n°10.
308        const GROUP_10 = 1 << 9;
309        /// The group n°11.
310        const GROUP_11 = 1 << 10;
311        /// The group n°12.
312        const GROUP_12 = 1 << 11;
313        /// The group n°13.
314        const GROUP_13 = 1 << 12;
315        /// The group n°14.
316        const GROUP_14 = 1 << 13;
317        /// The group n°15.
318        const GROUP_15 = 1 << 14;
319        /// The group n°16.
320        const GROUP_16 = 1 << 15;
321        /// The group n°17.
322        const GROUP_17 = 1 << 16;
323        /// The group n°18.
324        const GROUP_18 = 1 << 17;
325        /// The group n°19.
326        const GROUP_19 = 1 << 18;
327        /// The group n°20.
328        const GROUP_20 = 1 << 19;
329        /// The group n°21.
330        const GROUP_21 = 1 << 20;
331        /// The group n°22.
332        const GROUP_22 = 1 << 21;
333        /// The group n°23.
334        const GROUP_23 = 1 << 22;
335        /// The group n°24.
336        const GROUP_24 = 1 << 23;
337        /// The group n°25.
338        const GROUP_25 = 1 << 24;
339        /// The group n°26.
340        const GROUP_26 = 1 << 25;
341        /// The group n°27.
342        const GROUP_27 = 1 << 26;
343        /// The group n°28.
344        const GROUP_28 = 1 << 27;
345        /// The group n°29.
346        const GROUP_29 = 1 << 28;
347        /// The group n°30.
348        const GROUP_30 = 1 << 29;
349        /// The group n°31.
350        const GROUP_31 = 1 << 30;
351        /// The group n°32.
352        const GROUP_32 = 1 << 31;
353
354        /// All of the groups.
355        const ALL = u32::MAX;
356        /// None of the groups.
357        const NONE = 0;
358    }
359}
360
361impl Default for Group {
362    fn default() -> Self {
363        Group::ALL
364    }
365}
366
367/// Pairwise collision filtering using bit masks.
368///
369/// This filtering method is based on two 32-bit values:
370/// - The interaction groups memberships.
371/// - The interaction groups filter.
372///
373/// An interaction is allowed between two filters `a` and `b` when two conditions
374/// are met simultaneously:
375/// - The groups membership of `a` has at least one bit set to `1` in common with the groups filter of `b`.
376/// - The groups membership of `b` has at least one bit set to `1` in common with the groups filter of `a`.
377///
378/// In other words, interactions are allowed between two filter iff. the following condition is met:
379/// ```ignore
380/// (self.memberships & rhs.filter) != 0 && (rhs.memberships & self.filter) != 0
381/// ```
382#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Component, Reflect)]
383#[reflect(Component, Default, Hash, PartialEq)]
384#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
385pub struct CollisionGroups {
386    /// Groups memberships.
387    pub memberships: Group,
388    /// Groups filter.
389    pub filters: Group,
390}
391
392impl CollisionGroups {
393    /// Creates a new collision-groups with the given membership masks and filter masks.
394    pub const fn new(memberships: Group, filters: Group) -> Self {
395        Self {
396            memberships,
397            filters,
398        }
399    }
400}
401
402impl From<CollisionGroups> for InteractionGroups {
403    fn from(collision_groups: CollisionGroups) -> InteractionGroups {
404        InteractionGroups {
405            memberships: rapier::geometry::Group::from_bits(collision_groups.memberships.bits())
406                .unwrap(),
407            filter: rapier::geometry::Group::from_bits(collision_groups.filters.bits()).unwrap(),
408        }
409    }
410}
411
412/// Pairwise constraints resolution filtering using bit masks.
413///
414/// This follows the same rules as the `CollisionGroups`.
415#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash, Component, Reflect)]
416#[reflect(Component, Default, Hash, PartialEq)]
417#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
418pub struct SolverGroups {
419    /// Groups memberships.
420    pub memberships: Group,
421    /// Groups filter.
422    pub filters: Group,
423}
424
425impl SolverGroups {
426    /// Creates a new collision-groups with the given membership masks and filter masks.
427    pub const fn new(memberships: Group, filters: Group) -> Self {
428        Self {
429            memberships,
430            filters,
431        }
432    }
433}
434
435impl From<SolverGroups> for InteractionGroups {
436    fn from(solver_groups: SolverGroups) -> InteractionGroups {
437        InteractionGroups {
438            memberships: rapier::geometry::Group::from_bits(solver_groups.memberships.bits())
439                .unwrap(),
440            filter: rapier::geometry::Group::from_bits(solver_groups.filters.bits()).unwrap(),
441        }
442    }
443}
444
445#[derive(Default, Component, Reflect, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
446#[reflect(Component, Default)]
447#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
448/// Flags affecting the behavior of the constraints solver for a given contact manifold.
449pub struct ActiveHooks(u32);
450
451bitflags::bitflags! {
452    impl ActiveHooks: u32 {
453        /// If set, Rapier will call `PhysicsHooks::filter_contact_pair` whenever relevant.
454        const FILTER_CONTACT_PAIRS = 0b0001;
455        /// If set, Rapier will call `PhysicsHooks::filter_intersection_pair` whenever relevant.
456        const FILTER_INTERSECTION_PAIR = 0b0010;
457        /// If set, Rapier will call `PhysicsHooks::modify_solver_contact` whenever relevant.
458        const MODIFY_SOLVER_CONTACTS = 0b0100;
459    }
460}
461
462impl From<ActiveHooks> for rapier::pipeline::ActiveHooks {
463    fn from(active_hooks: ActiveHooks) -> rapier::pipeline::ActiveHooks {
464        rapier::pipeline::ActiveHooks::from_bits(active_hooks.bits())
465            .expect("Internal error: invalid active events conversion.")
466    }
467}
468
469#[derive(Default, Component, Reflect, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
470#[reflect(Component, Default)]
471#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
472/// Flags affecting the events generated for this [`Collider`].
473pub struct ActiveEvents(u32);
474
475bitflags::bitflags! {
476    impl ActiveEvents: u32 {
477        /// If set, Rapier will call `EventHandler::handle_collision_event`
478        /// whenever relevant for this [`Collider`].
479        const COLLISION_EVENTS = 0b0001;
480        /// If set, Rapier will call `EventHandler::handle_contact_force_event`
481        /// whenever relevant for this [`Collider`].
482        const CONTACT_FORCE_EVENTS = 0b0010;
483    }
484}
485
486impl From<ActiveEvents> for rapier::pipeline::ActiveEvents {
487    fn from(active_events: ActiveEvents) -> rapier::pipeline::ActiveEvents {
488        rapier::pipeline::ActiveEvents::from_bits(active_events.bits())
489            .expect("Internal error: invalid active events conversion.")
490    }
491}
492
493/// The total force magnitude beyond which a [`ContactForceEvent`] can be emitted.
494///
495/// This requires that the [`ActiveEvents::CONTACT_FORCE_EVENTS`] flag is set on the
496/// entity.
497#[derive(Copy, Clone, PartialEq, Component, Reflect)]
498#[reflect(Component, Default)]
499#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
500pub struct ContactForceEventThreshold(pub f32);
501
502impl Default for ContactForceEventThreshold {
503    fn default() -> Self {
504        Self(f32::MAX)
505    }
506}
507
508/// Sets the contact skin of the collider.
509///
510/// The contact skin acts as if the collider was enlarged with a skin of width `skin_thickness`
511/// around it, keeping objects further apart when colliding.
512///
513/// A non-zero contact skin can increase performance, and in some cases, stability. However
514/// it creates a small gap between colliding object (equal to the sum of their skin). If the
515/// skin is sufficiently small, this might not be visually significant or can be hidden by the
516/// rendering assets.
517#[derive(Copy, Clone, PartialEq, Default, Component, Reflect)]
518#[reflect(Component, Default)]
519#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
520pub struct ContactSkin(pub f32);
521
522/// Component which will be filled (if present) with a list of entities with which the current
523/// entity is currently in contact.
524///
525/// This currently only updates when on an entity with a `Collider`, and if the
526/// [`ActiveEvents::COLLISION_EVENTS`] is set on this entity or the entity it
527/// collided with.
528#[derive(Component, Default, Reflect)]
529#[reflect(Component, Default)]
530pub struct CollidingEntities(pub(crate) HashSet<Entity>);
531
532impl CollidingEntities {
533    /// Returns the number of colliding entities.
534    #[must_use]
535    pub fn len(&self) -> usize {
536        self.0.len()
537    }
538
539    /// Returns `true` if there is no colliding entities.
540    #[must_use]
541    pub fn is_empty(&self) -> bool {
542        self.0.is_empty()
543    }
544
545    /// Returns `true` if the collisions contains the specified entity.
546    #[must_use]
547    pub fn contains(&self, entity: Entity) -> bool {
548        self.0.contains(&entity)
549    }
550
551    /// An iterator visiting all colliding entities in arbitrary order.
552    pub fn iter(&self) -> impl Iterator<Item = Entity> + '_ {
553        self.0.iter().copied()
554    }
555}
556
557/// Indicates whether or not the collider is disabled explicitly by the user.
558#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Component, Reflect)]
559#[reflect(Component, Default, PartialEq)]
560pub struct ColliderDisabled;
561
562/// We restrict the scaling increment to 1.0e-4, to avoid numerical jitter
563/// due to the extraction of scaling factor from the GlobalTransform matrix.
564pub fn get_snapped_scale(scale: Vect) -> Vect {
565    fn snap_value(new: f32) -> f32 {
566        const PRECISION: f32 = 1.0e4;
567        (new * PRECISION).round() / PRECISION
568    }
569
570    Vect {
571        x: snap_value(scale.x),
572        y: snap_value(scale.y),
573        #[cfg(feature = "dim3")]
574        z: snap_value(scale.z),
575    }
576}