avian3d/collision/collider/mod.rs
1//! Components, traits, and plugins related to collider functionality.
2
3use crate::prelude::*;
4use bevy::{
5 ecs::{
6 component::Mutable,
7 entity::{hash_set::EntityHashSet, EntityMapper, MapEntities},
8 system::{ReadOnlySystemParam, SystemParam, SystemParamItem},
9 },
10 prelude::*,
11};
12use derive_more::From;
13
14mod backend;
15
16pub use backend::{ColliderBackendPlugin, ColliderMarker};
17
18pub mod collider_hierarchy;
19pub mod collider_transform;
20
21mod layers;
22pub use layers::*;
23
24/// The default [`Collider`] that uses Parry.
25#[cfg(all(
26 feature = "default-collider",
27 any(feature = "parry-f32", feature = "parry-f64")
28))]
29mod parry;
30#[cfg(all(
31 feature = "default-collider",
32 any(feature = "parry-f32", feature = "parry-f64")
33))]
34pub use parry::*;
35
36mod world_query;
37pub use world_query::*;
38
39#[cfg(feature = "default-collider")]
40mod constructor;
41#[cfg(feature = "default-collider")]
42pub use constructor::{
43 ColliderConstructor, ColliderConstructorHierarchy, ColliderConstructorHierarchyConfig,
44};
45
46/// A trait for creating colliders from other types.
47pub trait IntoCollider<C: AnyCollider> {
48 /// Creates a collider from `self`.
49 fn collider(&self) -> C;
50}
51
52/// Context necessary to calculate [`ColliderAabb`]s for an [`AnyCollider`]
53#[derive(Deref)]
54pub struct AabbContext<'a, 'w, 's, T: ReadOnlySystemParam> {
55 /// The entity for which the aabb is being calculated
56 pub entity: Entity,
57 #[deref]
58 item: &'a SystemParamItem<'w, 's, T>,
59}
60
61impl<T: ReadOnlySystemParam> Clone for AabbContext<'_, '_, '_, T> {
62 fn clone(&self) -> Self {
63 Self {
64 entity: self.entity,
65 item: self.item,
66 }
67 }
68}
69
70impl<'a, 'w, 's, T: ReadOnlySystemParam> AabbContext<'a, 'w, 's, T> {
71 /// Construct an [`AabbContext`]
72 pub fn new(entity: Entity, item: &'a <T as SystemParam>::Item<'w, 's>) -> Self {
73 Self { entity, item }
74 }
75}
76
77impl AabbContext<'_, '_, '_, ()> {
78 fn fake() -> Self {
79 Self {
80 entity: Entity::PLACEHOLDER,
81 item: &(),
82 }
83 }
84}
85
86/// Context necessary to calculate [`ContactManifold`]s for a set of [`AnyCollider`]
87#[derive(Deref)]
88pub struct ContactManifoldContext<'a, 'w, 's, T: ReadOnlySystemParam> {
89 /// The first collider entity involved in the contact.
90 pub entity1: Entity,
91 /// The second collider entity involved in the contact.
92 pub entity2: Entity,
93 #[deref]
94 item: &'a SystemParamItem<'w, 's, T>,
95}
96
97impl<'a, 'w, 's, T: ReadOnlySystemParam> ContactManifoldContext<'a, 'w, 's, T> {
98 /// Construct a [`ContactManifoldContext`]
99 pub fn new(
100 entity1: Entity,
101 entity2: Entity,
102 item: &'a <T as SystemParam>::Item<'w, 's>,
103 ) -> Self {
104 Self {
105 entity1,
106 entity2,
107 item,
108 }
109 }
110}
111
112impl ContactManifoldContext<'_, '_, '_, ()> {
113 fn fake() -> Self {
114 Self {
115 entity1: Entity::PLACEHOLDER,
116 entity2: Entity::PLACEHOLDER,
117 item: &(),
118 }
119 }
120}
121
122/// A trait that generalizes over colliders. Implementing this trait
123/// allows colliders to be used with the physics engine.
124pub trait AnyCollider: Component<Mutability = Mutable> + ComputeMassProperties {
125 /// A type providing additional context for collider operations.
126 ///
127 /// `Context` allows you to access an arbitrary [`ReadOnlySystemParam`] on
128 /// the world, for context-sensitive behavior in collider operations. You
129 /// can use this to query components on the collider entity, or get any
130 /// other necessary context from the world.
131 ///
132 /// # Example
133 ///
134 /// ```
135 #[cfg_attr(
136 feature = "2d",
137 doc = "# use avian2d::{prelude::*, math::{Vector, Scalar}};"
138 )]
139 #[cfg_attr(
140 feature = "3d",
141 doc = "# use avian3d::{prelude::*, math::{Vector, Scalar}};"
142 )]
143 /// # use bevy::prelude::*;
144 /// # use bevy::ecs::system::{SystemParam, lifetimeless::{SRes, SQuery}};
145 /// #
146 /// #[derive(Component)]
147 /// pub struct VoxelCollider;
148 ///
149 /// #[derive(Component)]
150 /// pub struct VoxelData {
151 /// // collider voxel data...
152 /// }
153 ///
154 /// # impl ComputeMassProperties2d for VoxelCollider {
155 /// # fn mass(&self, density: f32) -> f32 {0.}
156 /// # fn unit_angular_inertia(&self) -> f32 { 0.}
157 /// # fn center_of_mass(&self) -> Vec2 { Vec2::ZERO }
158 /// # }
159 /// #
160 /// # impl ComputeMassProperties3d for VoxelCollider {
161 /// # fn mass(&self, density: f32) -> f32 {0.}
162 /// # fn unit_principal_angular_inertia(&self) -> Vec3 { Vec3::ZERO }
163 /// # fn center_of_mass(&self) -> Vec3 { Vec3::ZERO }
164 /// # }
165 /// #
166 /// impl AnyCollider for VoxelCollider {
167 /// type Context = (
168 /// // you can query extra components here
169 /// SQuery<&'static VoxelData>,
170 /// // or put any other read-only system param here
171 /// SRes<Time>,
172 /// );
173 ///
174 /// # fn aabb_with_context(
175 /// # &self,
176 /// # _: Vector,
177 /// # _: impl Into<Rotation>,
178 /// # _: AabbContext<Self::Context>,
179 /// # ) -> ColliderAabb { unimplemented!() }
180 /// #
181 /// fn contact_manifolds_with_context(
182 /// &self,
183 /// other: &Self,
184 /// position1: Vector,
185 /// rotation1: impl Into<Rotation>,
186 /// position2: Vector,
187 /// rotation2: impl Into<Rotation>,
188 /// prediction_distance: Scalar,
189 /// manifolds: &mut Vec<ContactManifold>,
190 /// context: ContactManifoldContext<Self::Context>,
191 /// ) {
192 /// let [voxels1, voxels2] = context.0.get_many([context.entity1, context.entity2])
193 /// .expect("our own `VoxelCollider` entities should have `VoxelData`");
194 /// let elapsed = context.1.elapsed();
195 /// // do some computation...
196 /// # unimplemented!()
197 /// }
198 /// }
199 /// ```
200 type Context: for<'w, 's> ReadOnlySystemParam<Item<'w, 's>: Send + Sync>;
201
202 /// Computes the [Axis-Aligned Bounding Box](ColliderAabb) of the collider
203 /// with the given position and rotation.
204 ///
205 /// See [`SimpleCollider::aabb`] for collider types with empty [`AnyCollider::Context`]
206 #[cfg_attr(
207 feature = "2d",
208 doc = "\n\nThe rotation is counterclockwise and in radians."
209 )]
210 fn aabb_with_context(
211 &self,
212 position: Vector,
213 rotation: impl Into<Rotation>,
214 context: AabbContext<Self::Context>,
215 ) -> ColliderAabb;
216
217 /// Computes the swept [Axis-Aligned Bounding Box](ColliderAabb) of the collider.
218 /// This corresponds to the space the shape would occupy if it moved from the given
219 /// start position to the given end position.
220 ///
221 /// See [`SimpleCollider::swept_aabb`] for collider types with empty [`AnyCollider::Context`]
222 #[cfg_attr(
223 feature = "2d",
224 doc = "\n\nThe rotation is counterclockwise and in radians."
225 )]
226 fn swept_aabb_with_context(
227 &self,
228 start_position: Vector,
229 start_rotation: impl Into<Rotation>,
230 end_position: Vector,
231 end_rotation: impl Into<Rotation>,
232 context: AabbContext<Self::Context>,
233 ) -> ColliderAabb {
234 self.aabb_with_context(start_position, start_rotation, context.clone())
235 .merged(self.aabb_with_context(end_position, end_rotation, context))
236 }
237
238 /// Computes all [`ContactManifold`]s between two colliders.
239 ///
240 /// Returns an empty vector if the colliders are separated by a distance greater than `prediction_distance`
241 /// or if the given shapes are invalid.
242 ///
243 /// See [`SimpleCollider::contact_manifolds`] for collider types with empty [`AnyCollider::Context`]
244 fn contact_manifolds_with_context(
245 &self,
246 other: &Self,
247 position1: Vector,
248 rotation1: impl Into<Rotation>,
249 position2: Vector,
250 rotation2: impl Into<Rotation>,
251 prediction_distance: Scalar,
252 manifolds: &mut Vec<ContactManifold>,
253 context: ContactManifoldContext<Self::Context>,
254 );
255}
256
257/// A simplified wrapper around [`AnyCollider`] that doesn't require passing in the context for
258/// implementations that don't need context
259pub trait SimpleCollider: AnyCollider<Context = ()> {
260 /// Computes the [Axis-Aligned Bounding Box](ColliderAabb) of the collider
261 /// with the given position and rotation.
262 ///
263 /// See [`AnyCollider::aabb_with_context`] for collider types with non-empty [`AnyCollider::Context`]
264 fn aabb(&self, position: Vector, rotation: impl Into<Rotation>) -> ColliderAabb {
265 self.aabb_with_context(position, rotation, AabbContext::fake())
266 }
267
268 /// Computes the swept [Axis-Aligned Bounding Box](ColliderAabb) of the collider.
269 /// This corresponds to the space the shape would occupy if it moved from the given
270 /// start position to the given end position.
271 ///
272 /// See [`AnyCollider::swept_aabb_with_context`] for collider types with non-empty [`AnyCollider::Context`]
273 fn swept_aabb(
274 &self,
275 start_position: Vector,
276 start_rotation: impl Into<Rotation>,
277 end_position: Vector,
278 end_rotation: impl Into<Rotation>,
279 ) -> ColliderAabb {
280 self.swept_aabb_with_context(
281 start_position,
282 start_rotation,
283 end_position,
284 end_rotation,
285 AabbContext::fake(),
286 )
287 }
288
289 /// Computes all [`ContactManifold`]s between two colliders, writing the results into `manifolds`.
290 ///
291 /// `manifolds` is cleared if the colliders are separated by a distance greater than `prediction_distance`
292 /// or if the given shapes are invalid.
293 ///
294 /// See [`AnyCollider::contact_manifolds_with_context`] for collider types with non-empty [`AnyCollider::Context`]
295 fn contact_manifolds(
296 &self,
297 other: &Self,
298 position1: Vector,
299 rotation1: impl Into<Rotation>,
300 position2: Vector,
301 rotation2: impl Into<Rotation>,
302 prediction_distance: Scalar,
303 manifolds: &mut Vec<ContactManifold>,
304 ) {
305 self.contact_manifolds_with_context(
306 other,
307 position1,
308 rotation1,
309 position2,
310 rotation2,
311 prediction_distance,
312 manifolds,
313 ContactManifoldContext::fake(),
314 )
315 }
316}
317
318impl<C: AnyCollider<Context = ()>> SimpleCollider for C {}
319
320/// A trait for colliders that support scaling.
321pub trait ScalableCollider: AnyCollider {
322 /// Returns the global scaling factor of the collider.
323 fn scale(&self) -> Vector;
324
325 /// Sets the global scaling factor of the collider.
326 ///
327 /// If the scaling factor is not uniform and the resulting scaled shape
328 /// can not be represented exactly, the given `detail` is used for an approximation.
329 fn set_scale(&mut self, scale: Vector, detail: u32);
330
331 /// Scales the collider by the given scaling factor.
332 ///
333 /// If the scaling factor is not uniform and the resulting scaled shape
334 /// can not be represented exactly, the given `detail` is used for an approximation.
335 fn scale_by(&mut self, factor: Vector, detail: u32) {
336 self.set_scale(factor * self.scale(), detail)
337 }
338}
339
340/// A marker component that indicates that a [collider](Collider) is disabled
341/// and should not detect collisions or be included in spatial queries.
342///
343/// This is useful for temporarily disabling a collider without removing it from the world.
344/// To re-enable the collider, simply remove this component.
345///
346/// Note that a disabled collider will still contribute to the mass properties of the rigid body
347/// it is attached to. Set the [`Mass`] of the collider to zero to prevent this.
348///
349/// [`ColliderDisabled`] only applies to the entity it is attached to, not its children.
350///
351/// # Example
352///
353/// ```
354#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
355#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
356/// # use bevy::prelude::*;
357/// #
358/// #[derive(Component)]
359/// pub struct Character;
360///
361/// /// Disables colliders for all rigid body characters, for example during cutscenes.
362/// fn disable_character_colliders(
363/// mut commands: Commands,
364/// query: Query<Entity, (With<RigidBody>, With<Character>)>,
365/// ) {
366/// for entity in &query {
367/// commands.entity(entity).insert(ColliderDisabled);
368/// }
369/// }
370///
371/// /// Enables colliders for all rigid body characters.
372/// fn enable_character_colliders(
373/// mut commands: Commands,
374/// query: Query<Entity, (With<RigidBody>, With<Character>)>,
375/// ) {
376/// for entity in &query {
377/// commands.entity(entity).remove::<ColliderDisabled>();
378/// }
379/// }
380/// ```
381///
382/// # Related Components
383///
384/// - [`RigidBodyDisabled`]: Disables a rigid body.
385/// - [`JointDisabled`]: Disables a joint constraint.
386#[derive(Reflect, Clone, Copy, Component, Debug, Default)]
387#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
388#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
389#[reflect(Debug, Component, Default)]
390pub struct ColliderDisabled;
391
392/// A component that marks a [`Collider`] as a sensor, also known as a trigger.
393///
394/// Sensor colliders send [collision events](crate::collision#collision-events) and register intersections,
395/// but allow other bodies to pass through them. This is often used to detect when something enters
396/// or leaves an area or is intersecting some shape.
397///
398/// Sensor colliders do *not* contribute to the mass properties of rigid bodies.
399///
400/// # Example
401///
402/// ```
403#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
404#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
405/// use bevy::prelude::*;
406///
407/// fn setup(mut commands: Commands) {
408/// // Spawn a static body with a sensor collider.
409/// // Other bodies will pass through, but it will still send collision events.
410#[cfg_attr(
411 feature = "2d",
412 doc = " commands.spawn((RigidBody::Static, Collider::circle(0.5), Sensor));"
413)]
414#[cfg_attr(
415 feature = "3d",
416 doc = " commands.spawn((RigidBody::Static, Collider::sphere(0.5), Sensor));"
417)]
418/// }
419/// ```
420#[doc(alias = "Trigger")]
421#[derive(Reflect, Clone, Component, Debug, Default, PartialEq, Eq)]
422#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
423#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
424#[reflect(Debug, Component, Default, PartialEq)]
425pub struct Sensor;
426
427/// The Axis-Aligned Bounding Box of a [collider](Collider) in world space.
428///
429/// Note that the AABB will be [`ColliderAabb::INVALID`] until the first physics update.
430#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq)]
431#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
432#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
433#[reflect(Debug, Component, PartialEq)]
434pub struct ColliderAabb {
435 /// The minimum point of the AABB.
436 pub min: Vector,
437 /// The maximum point of thr AABB.
438 pub max: Vector,
439}
440
441impl Default for ColliderAabb {
442 fn default() -> Self {
443 ColliderAabb::INVALID
444 }
445}
446
447impl ColliderAabb {
448 /// An invalid [`ColliderAabb`] that represents an empty AABB.
449 pub const INVALID: Self = Self {
450 min: Vector::INFINITY,
451 max: Vector::NEG_INFINITY,
452 };
453
454 /// Creates a new [`ColliderAabb`] from the given `center` and `half_size`.
455 pub fn new(center: Vector, half_size: Vector) -> Self {
456 Self {
457 min: center - half_size,
458 max: center + half_size,
459 }
460 }
461
462 /// Creates a new [`ColliderAabb`] from its minimum and maximum points.
463 pub fn from_min_max(min: Vector, max: Vector) -> Self {
464 Self { min, max }
465 }
466
467 /// Creates a new [`ColliderAabb`] from a given `SharedShape`.
468 #[cfg(all(
469 feature = "default-collider",
470 any(feature = "parry-f32", feature = "parry-f64")
471 ))]
472 pub fn from_shape(shape: &crate::parry::shape::SharedShape) -> Self {
473 let aabb = shape.compute_local_aabb();
474 Self {
475 min: aabb.mins.into(),
476 max: aabb.maxs.into(),
477 }
478 }
479
480 /// Computes the center of the AABB,
481 #[inline(always)]
482 pub fn center(self) -> Vector {
483 self.min.midpoint(self.max)
484 }
485
486 /// Computes the size of the AABB.
487 #[inline(always)]
488 pub fn size(self) -> Vector {
489 self.max - self.min
490 }
491
492 /// Merges this AABB with another one.
493 #[inline(always)]
494 pub fn merged(self, other: Self) -> Self {
495 ColliderAabb {
496 min: self.min.min(other.min),
497 max: self.max.max(other.max),
498 }
499 }
500
501 /// Increases the size of the bounding volume in each direction by the given amount.
502 #[inline(always)]
503 pub fn grow(&self, amount: Vector) -> Self {
504 let b = Self {
505 min: self.min - amount,
506 max: self.max + amount,
507 };
508 debug_assert!(b.min.cmple(b.max).all());
509 b
510 }
511
512 /// Decreases the size of the bounding volume in each direction by the given amount.
513 #[inline(always)]
514 pub fn shrink(&self, amount: Vector) -> Self {
515 let b = Self {
516 min: self.min + amount,
517 max: self.max - amount,
518 };
519 debug_assert!(b.min.cmple(b.max).all());
520 b
521 }
522
523 /// Checks if `self` intersects with `other`.
524 #[inline(always)]
525 #[cfg(feature = "2d")]
526 pub fn intersects(&self, other: &Self) -> bool {
527 let x_overlaps = self.min.x <= other.max.x && self.max.x >= other.min.x;
528 let y_overlaps = self.min.y <= other.max.y && self.max.y >= other.min.y;
529 x_overlaps && y_overlaps
530 }
531
532 /// Checks if `self` intersects with `other`.
533 #[inline(always)]
534 #[cfg(feature = "3d")]
535 pub fn intersects(&self, other: &Self) -> bool {
536 let x_overlaps = self.min.x <= other.max.x && self.max.x >= other.min.x;
537 let y_overlaps = self.min.y <= other.max.y && self.max.y >= other.min.y;
538 let z_overlaps = self.min.z <= other.max.z && self.max.z >= other.min.z;
539 x_overlaps && y_overlaps && z_overlaps
540 }
541}
542
543/// A component that adds an extra margin or "skin" around [`Collider`] shapes to help maintain
544/// additional separation to other objects. This added thickness can help improve
545/// stability and performance in some cases, especially for thin shapes such as trimeshes.
546///
547/// There are three primary reasons for collision margins:
548///
549/// 1. Collision detection is often more efficient when shapes are not overlapping
550/// further than their collision margins. Deeply overlapping shapes require
551/// more expensive collision algorithms.
552///
553/// 2. Some shapes such as triangles and planes are infinitely thin,
554/// which can cause precision errors. A collision margin adds artificial
555/// thickness to shapes, improving stability.
556///
557/// 3. Overall, collision margins give the physics engine more
558/// room for error when resolving contacts. This can also help
559/// prevent visible artifacts such as objects poking through the ground.
560///
561/// If a rigid body with a [`CollisionMargin`] has colliders as child entities,
562/// and those colliders don't have their own [`CollisionMargin`] components,
563/// the colliders will use the rigid body's [`CollisionMargin`].
564///
565/// # Example
566///
567/// ```
568#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
569#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
570/// use bevy::prelude::*;
571///
572/// fn setup(mut commands: Commands) {
573#[cfg_attr(
574 feature = "2d",
575 doc = " // Spawn a rigid body with a collider.
576 // A margin of `0.1` is added around the shape.
577 commands.spawn((
578 RigidBody::Dynamic,
579 Collider::capsule(2.0, 0.5),
580 CollisionMargin(0.1),
581 ));"
582)]
583#[cfg_attr(
584 feature = "3d",
585 doc = " let mesh = Mesh::from(Torus::default());
586
587 // Spawn a rigid body with a triangle mesh collider.
588 // A margin of `0.1` is added around the shape.
589 commands.spawn((
590 RigidBody::Dynamic,
591 Collider::trimesh_from_mesh(&mesh).unwrap(),
592 CollisionMargin(0.1),
593 ));"
594)]
595/// }
596/// ```
597#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
598#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
599#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
600#[reflect(Component)]
601#[doc(alias = "ContactSkin")]
602pub struct CollisionMargin(pub Scalar);
603
604/// A component for reading which entities are colliding with a collider entity.
605/// Must be added manually for desired colliders.
606///
607/// # Example
608///
609/// ```
610#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
611#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
612/// use bevy::prelude::*;
613///
614/// fn setup(mut commands: Commands) {
615/// commands.spawn((
616/// RigidBody::Dynamic,
617/// Collider::capsule(0.5, 1.5),
618/// // Add the `CollidingEntities` component to read entities colliding with this entity.
619/// CollidingEntities::default(),
620/// ));
621/// }
622///
623/// fn my_system(query: Query<(Entity, &CollidingEntities)>) {
624/// for (entity, colliding_entities) in &query {
625/// println!(
626/// "{} is colliding with the following entities: {:?}",
627/// entity,
628/// colliding_entities,
629/// );
630/// }
631/// }
632/// ```
633#[derive(Reflect, Clone, Component, Debug, Default, Deref, DerefMut, PartialEq, Eq)]
634#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
635#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
636#[reflect(Debug, Component, Default, PartialEq)]
637pub struct CollidingEntities(pub EntityHashSet);
638
639impl MapEntities for CollidingEntities {
640 fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
641 self.0 = self
642 .0
643 .clone()
644 .into_iter()
645 .map(|e| entity_mapper.get_mapped(e))
646 .collect()
647 }
648}