bevy_rapier3d/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, InteractionTestMode, 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            test_mode: InteractionTestMode::And,
409        }
410    }
411}
412
413/// Pairwise constraints resolution filtering using bit masks.
414///
415/// This follows the same rules as the `CollisionGroups`.
416#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash, Component, Reflect)]
417#[reflect(Component, Default, Hash, PartialEq)]
418#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
419pub struct SolverGroups {
420    /// Groups memberships.
421    pub memberships: Group,
422    /// Groups filter.
423    pub filters: Group,
424}
425
426impl SolverGroups {
427    /// Creates a new collision-groups with the given membership masks and filter masks.
428    pub const fn new(memberships: Group, filters: Group) -> Self {
429        Self {
430            memberships,
431            filters,
432        }
433    }
434}
435
436impl From<SolverGroups> for InteractionGroups {
437    fn from(solver_groups: SolverGroups) -> InteractionGroups {
438        InteractionGroups {
439            memberships: rapier::geometry::Group::from_bits(solver_groups.memberships.bits())
440                .unwrap(),
441            filter: rapier::geometry::Group::from_bits(solver_groups.filters.bits()).unwrap(),
442            test_mode: InteractionTestMode::And,
443        }
444    }
445}
446
447#[derive(Default, Component, Reflect, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
448#[reflect(Component, Default)]
449#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
450/// Flags affecting the behavior of the constraints solver for a given contact manifold.
451pub struct ActiveHooks(u32);
452
453bitflags::bitflags! {
454    impl ActiveHooks: u32 {
455        /// If set, Rapier will call `PhysicsHooks::filter_contact_pair` whenever relevant.
456        const FILTER_CONTACT_PAIRS = 0b0001;
457        /// If set, Rapier will call `PhysicsHooks::filter_intersection_pair` whenever relevant.
458        const FILTER_INTERSECTION_PAIR = 0b0010;
459        /// If set, Rapier will call `PhysicsHooks::modify_solver_contact` whenever relevant.
460        const MODIFY_SOLVER_CONTACTS = 0b0100;
461    }
462}
463
464impl From<ActiveHooks> for rapier::pipeline::ActiveHooks {
465    fn from(active_hooks: ActiveHooks) -> rapier::pipeline::ActiveHooks {
466        rapier::pipeline::ActiveHooks::from_bits(active_hooks.bits())
467            .expect("Internal error: invalid active events conversion.")
468    }
469}
470
471#[derive(Default, Component, Reflect, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
472#[reflect(Component, Default)]
473#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
474/// Flags affecting the events generated for this [`Collider`].
475pub struct ActiveEvents(u32);
476
477bitflags::bitflags! {
478    impl ActiveEvents: u32 {
479        /// If set, Rapier will call `EventHandler::handle_collision_event`
480        /// whenever relevant for this [`Collider`].
481        const COLLISION_EVENTS = 0b0001;
482        /// If set, Rapier will call `EventHandler::handle_contact_force_event`
483        /// whenever relevant for this [`Collider`].
484        const CONTACT_FORCE_EVENTS = 0b0010;
485    }
486}
487
488impl From<ActiveEvents> for rapier::pipeline::ActiveEvents {
489    fn from(active_events: ActiveEvents) -> rapier::pipeline::ActiveEvents {
490        rapier::pipeline::ActiveEvents::from_bits(active_events.bits())
491            .expect("Internal error: invalid active events conversion.")
492    }
493}
494
495/// The total force magnitude beyond which a [`ContactForceEvent`] can be emitted.
496///
497/// This requires that the [`ActiveEvents::CONTACT_FORCE_EVENTS`] flag is set on the
498/// entity.
499#[derive(Copy, Clone, PartialEq, Component, Reflect)]
500#[reflect(Component, Default)]
501#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
502pub struct ContactForceEventThreshold(pub f32);
503
504impl Default for ContactForceEventThreshold {
505    fn default() -> Self {
506        Self(f32::MAX)
507    }
508}
509
510/// Sets the contact skin of the collider.
511///
512/// The contact skin acts as if the collider was enlarged with a skin of width `skin_thickness`
513/// around it, keeping objects further apart when colliding.
514///
515/// A non-zero contact skin can increase performance, and in some cases, stability. However
516/// it creates a small gap between colliding object (equal to the sum of their skin). If the
517/// skin is sufficiently small, this might not be visually significant or can be hidden by the
518/// rendering assets.
519#[derive(Copy, Clone, PartialEq, Default, Component, Reflect)]
520#[reflect(Component, Default)]
521#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
522pub struct ContactSkin(pub f32);
523
524/// Component which will be filled (if present) with a list of entities with which the current
525/// entity is currently in contact.
526///
527/// This currently only updates when on an entity with a `Collider`, and if the
528/// [`ActiveEvents::COLLISION_EVENTS`] is set on this entity or the entity it
529/// collided with.
530#[derive(Component, Default, Reflect)]
531#[reflect(Component, Default)]
532pub struct CollidingEntities(pub(crate) HashSet<Entity>);
533
534impl CollidingEntities {
535    /// Returns the number of colliding entities.
536    #[must_use]
537    pub fn len(&self) -> usize {
538        self.0.len()
539    }
540
541    /// Returns `true` if there is no colliding entities.
542    #[must_use]
543    pub fn is_empty(&self) -> bool {
544        self.0.is_empty()
545    }
546
547    /// Returns `true` if the collisions contains the specified entity.
548    #[must_use]
549    pub fn contains(&self, entity: Entity) -> bool {
550        self.0.contains(&entity)
551    }
552
553    /// An iterator visiting all colliding entities in arbitrary order.
554    pub fn iter(&self) -> impl Iterator<Item = Entity> + '_ {
555        self.0.iter().copied()
556    }
557}
558
559/// Indicates whether or not the collider is disabled explicitly by the user.
560#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Component, Reflect)]
561#[reflect(Component, Default, PartialEq)]
562pub struct ColliderDisabled;
563
564/// We restrict the scaling increment to 1.0e-4, to avoid numerical jitter
565/// due to the extraction of scaling factor from the GlobalTransform matrix.
566pub fn get_snapped_scale(scale: Vect) -> Vect {
567    fn snap_value(new: f32) -> f32 {
568        const PRECISION: f32 = 1.0e4;
569        (new * PRECISION).round() / PRECISION
570    }
571
572    Vect {
573        x: snap_value(scale.x),
574        y: snap_value(scale.y),
575        #[cfg(feature = "dim3")]
576        z: snap_value(scale.z),
577    }
578}