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::{EntityMapper, MapEntities, hash_set::EntityHashSet},
8 system::{ReadOnlySystemParam, SystemParam, SystemParamItem},
9 },
10 prelude::*,
11};
12use derive_more::From;
13
14mod backend;
15
16pub use backend::{ColliderBackendPlugin, ColliderMarker};
17
18#[cfg(all(feature = "collider-from-mesh", feature = "default-collider"))]
19mod cache;
20#[cfg(all(feature = "collider-from-mesh", feature = "default-collider"))]
21pub use cache::ColliderCachePlugin;
22pub mod collider_hierarchy;
23pub mod collider_transform;
24#[cfg(all(feature = "3d", any(feature = "parry-f32", feature = "parry-f64")))]
25pub mod trimesh_builder;
26
27mod layers;
28pub use layers::*;
29
30/// The default [`Collider`] that uses Parry.
31#[cfg(all(
32 feature = "default-collider",
33 any(feature = "parry-f32", feature = "parry-f64")
34))]
35mod parry;
36#[cfg(all(
37 feature = "default-collider",
38 any(feature = "parry-f32", feature = "parry-f64")
39))]
40pub use parry::*;
41
42#[cfg(feature = "default-collider")]
43mod constructor;
44#[cfg(feature = "default-collider")]
45pub use constructor::{
46 ColliderConstructor, ColliderConstructorHierarchy, ColliderConstructorHierarchyConfig,
47 ColliderConstructorHierarchyReady, ColliderConstructorReady,
48};
49
50/// A trait for creating colliders from other types.
51pub trait IntoCollider<C: AnyCollider> {
52 /// Creates a collider from `self`.
53 fn collider(&self) -> C;
54}
55
56/// Context necessary for operations involving an [`AnyCollider`].
57#[derive(Deref)]
58pub struct ColliderContext<'a, 'w, 's, T: ReadOnlySystemParam> {
59 /// The collider entity involved in the operation.
60 pub collider: Entity,
61 #[deref]
62 item: &'a SystemParamItem<'w, 's, T>,
63}
64
65impl<T: ReadOnlySystemParam> Clone for ColliderContext<'_, '_, '_, T> {
66 fn clone(&self) -> Self {
67 Self {
68 collider: self.collider,
69 item: self.item,
70 }
71 }
72}
73
74impl<'a, 'w, 's, T: ReadOnlySystemParam> ColliderContext<'a, 'w, 's, T> {
75 /// Constructs a [`ColliderContext`].
76 pub fn new(collider: Entity, item: &'a <T as SystemParam>::Item<'w, 's>) -> Self {
77 Self { collider, item }
78 }
79}
80
81impl ColliderContext<'_, '_, '_, ()> {
82 /// No context is needed for this collider.
83 const NO_CONTEXT: Self = Self {
84 collider: Entity::PLACEHOLDER,
85 item: &(),
86 };
87}
88
89/// Context necessary for operations involving a pair of [`AnyCollider`]s.
90#[derive(Deref)]
91pub struct ColliderPairContext<'a, 'w, 's, T: ReadOnlySystemParam> {
92 /// The first collider entity involved in the operation.
93 pub collider1: Entity,
94 /// The second collider entity involved in the operation.
95 pub collider2: Entity,
96 #[deref]
97 item: &'a SystemParamItem<'w, 's, T>,
98}
99
100impl<'a, 'w, 's, T: ReadOnlySystemParam> ColliderPairContext<'a, 'w, 's, T> {
101 /// Constructs a [`ColliderPairContext`].
102 pub fn new(
103 collider1: Entity,
104 collider2: Entity,
105 item: &'a <T as SystemParam>::Item<'w, 's>,
106 ) -> Self {
107 Self {
108 collider1,
109 collider2,
110 item,
111 }
112 }
113}
114
115impl ColliderPairContext<'_, '_, '_, ()> {
116 /// No context is needed for this collider pair.
117 const NO_CONTEXT: Self = Self {
118 collider1: Entity::PLACEHOLDER,
119 collider2: Entity::PLACEHOLDER,
120 item: &(),
121 };
122}
123
124/// A trait that generalizes over colliders. Implementing this trait
125/// allows colliders to be used with the physics engine.
126pub trait AnyCollider: Component<Mutability = Mutable> + ComputeMassProperties {
127 /// A type providing additional context for collider operations.
128 ///
129 /// `Context` allows you to access an arbitrary [`ReadOnlySystemParam`] on
130 /// the world, for context-sensitive behavior in collider operations. You
131 /// can use this to query components on the collider entity, or get any
132 /// other necessary context from the world.
133 ///
134 /// # Example
135 ///
136 /// ```
137 #[cfg_attr(feature = "2d", doc = "# use avian2d::{prelude::*, math::RVector};")]
138 #[cfg_attr(feature = "3d", doc = "# use avian3d::{prelude::*, math::RVector};")]
139 /// # use bevy::prelude::*;
140 /// # use bevy::ecs::system::{SystemParam, lifetimeless::{SRes, SQuery}};
141 /// #
142 /// #[derive(Component)]
143 /// pub struct VoxelCollider;
144 ///
145 /// #[derive(Component)]
146 /// pub struct VoxelData {
147 /// // collider voxel data...
148 /// }
149 ///
150 /// # impl ComputeMassProperties2d for VoxelCollider {
151 /// # fn mass(&self, density: f32) -> f32 {0.}
152 /// # fn unit_angular_inertia(&self) -> f32 { 0.}
153 /// # fn center_of_mass(&self) -> Vec2 { Vec2::ZERO }
154 /// # }
155 /// #
156 /// # impl ComputeMassProperties3d for VoxelCollider {
157 /// # fn mass(&self, density: f32) -> f32 {0.}
158 /// # fn unit_principal_angular_inertia(&self) -> Vec3 { Vec3::ZERO }
159 /// # fn center_of_mass(&self) -> Vec3 { Vec3::ZERO }
160 /// # }
161 /// #
162 /// impl AnyCollider for VoxelCollider {
163 /// type Context = (
164 /// // you can query extra components here
165 /// SQuery<&'static VoxelData>,
166 /// // or put any other read-only system param here
167 /// SRes<Time>,
168 /// );
169 ///
170 /// # fn aabb_with_context(
171 /// # &self,
172 /// # _: RVector,
173 #[cfg_attr(feature = "2d", doc = "# _: impl Into<Rot2>,")]
174 #[cfg_attr(feature = "3d", doc = "# _: impl Into<Quat>,")]
175 /// # _: f32,
176 /// # _: ColliderContext<Self::Context>,
177 /// # ) -> ColliderAabb { unimplemented!() }
178 /// #
179 /// fn contact_manifolds_with_context(
180 /// &self,
181 /// other: &Self,
182 /// position1: RVector,
183 #[cfg_attr(feature = "2d", doc = " rotation1: impl Into<Rot2>,")]
184 #[cfg_attr(feature = "3d", doc = " rotation1: impl Into<Quat>,")]
185 /// position2: RVector,
186 #[cfg_attr(feature = "2d", doc = " rotation2: impl Into<Rot2>,")]
187 #[cfg_attr(feature = "3d", doc = " rotation2: impl Into<Quat>,")]
188 /// prediction_distance: f32,
189 /// manifolds: &mut Vec<ContactManifold>,
190 /// context: ColliderPairContext<Self::Context>,
191 /// ) {
192 /// let [voxels1, voxels2] = context.0.get_many([context.collider1, context.collider2])
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, grown by the given margin.
204 ///
205 /// See [`SimpleCollider::aabb`] for collider types with an 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: RVector,
213 rotation: impl Into<Rot>,
214 margin: f32,
215 context: ColliderContext<Self::Context>,
216 ) -> ColliderAabb;
217
218 /// Computes the swept [Axis-Aligned Bounding Box](ColliderAabb) of the collider,
219 /// grown by the given margin. This corresponds to the space the shape would occupy
220 /// if it moved from the given start position to the given end position.
221 ///
222 /// See [`SimpleCollider::swept_aabb`] for collider types with an empty [`AnyCollider::Context`].
223 #[cfg_attr(
224 feature = "2d",
225 doc = "\n\nThe rotation is counterclockwise and in radians."
226 )]
227 fn swept_aabb_with_context(
228 &self,
229 start_position: RVector,
230 start_rotation: impl Into<Rot>,
231 end_position: RVector,
232 end_rotation: impl Into<Rot>,
233 margin: f32,
234 context: ColliderContext<Self::Context>,
235 ) -> ColliderAabb {
236 self.aabb_with_context(start_position, start_rotation, margin, context.clone())
237 .merged(self.aabb_with_context(end_position, end_rotation, margin, context))
238 }
239
240 /// Computes all [`ContactManifold`]s between two colliders.
241 ///
242 /// Returns an empty vector if the colliders are separated by a distance greater than `prediction_distance`
243 /// or if the given shapes are invalid.
244 ///
245 /// See [`SimpleCollider::contact_manifolds`] for collider types with an empty [`AnyCollider::Context`].
246 fn contact_manifolds_with_context(
247 &self,
248 other: &Self,
249 position1: RVector,
250 rotation1: impl Into<Rot>,
251 position2: RVector,
252 rotation2: impl Into<Rot>,
253 prediction_distance: f32,
254 manifolds: &mut Vec<ContactManifold>,
255 context: ColliderPairContext<Self::Context>,
256 );
257
258 /// Returns a conservative minimum thickness used by [Continuous Collision Detection (CCD)][CCD]
259 /// to determine when the collider might tunnel through other geometry.
260 ///
261 /// Typically corresponds to the minimum distance from the centroid
262 /// of any given shape to its surface.
263 ///
264 /// [CCD]: crate::dynamics::ccd
265 fn ccd_thickness_with_context(&self, context: ColliderContext<Self::Context>) -> f32 {
266 let aabb = self.aabb_with_context(RVector::ZERO, Rot::IDENTITY, 0.0, context);
267 aabb.size().min_element() * 0.5
268 }
269
270 /// Returns the maximum distance from the collider to the given point.
271 fn max_distance_to_point_with_context(
272 &self,
273 point: RVector,
274 context: ColliderContext<Self::Context>,
275 ) -> f32 {
276 let aabb = self.aabb_with_context(RVector::ZERO, Rot::IDENTITY, 0.0, context);
277 point.distance(aabb.center()).f32() + aabb.size().length() * 0.5
278 }
279
280 /// Returns the radius of the bounding sphere of the collider.
281 ///
282 /// This is used to compute a size-relative [`ColliderAabbMargin`] for the collider.
283 fn bounding_radius_with_context(&self, context: ColliderContext<Self::Context>) -> f32 {
284 // We don't have a bounding sphere method directly available here,
285 // so for now we settle for this approximation for the default implementation.
286 let aabb = self.aabb_with_context(RVector::ZERO, Rot::IDENTITY, 0.0, context.clone());
287 aabb.size().length() * 0.5
288 }
289}
290
291/// A simplified wrapper around [`AnyCollider`] that doesn't require passing in the context for
292/// implementations that don't need context
293pub trait SimpleCollider: AnyCollider<Context = ()> {
294 /// Computes the [Axis-Aligned Bounding Box](ColliderAabb) of the collider
295 /// with the given position and rotation, grown by the given margin.
296 ///
297 /// See [`AnyCollider::aabb_with_context`] for collider types with a non-empty [`AnyCollider::Context`].
298 fn aabb(&self, position: RVector, rotation: impl Into<Rot>, margin: f32) -> ColliderAabb {
299 self.aabb_with_context(position, rotation, margin, ColliderContext::NO_CONTEXT)
300 }
301
302 /// Computes the swept [Axis-Aligned Bounding Box](ColliderAabb) of the collider,
303 /// grown by the given margin. This corresponds to the space the shape would occupy
304 /// if it moved from the given start position to the given end position.
305 ///
306 /// See [`AnyCollider::swept_aabb_with_context`] for collider types with a non-empty [`AnyCollider::Context`].
307 fn swept_aabb(
308 &self,
309 start_position: RVector,
310 start_rotation: impl Into<Rot>,
311 end_position: RVector,
312 end_rotation: impl Into<Rot>,
313 margin: f32,
314 ) -> ColliderAabb {
315 self.swept_aabb_with_context(
316 start_position,
317 start_rotation,
318 end_position,
319 end_rotation,
320 margin,
321 ColliderContext::NO_CONTEXT,
322 )
323 }
324
325 /// Computes all [`ContactManifold`]s between two colliders, writing the results into `manifolds`.
326 ///
327 /// `manifolds` is cleared if the colliders are separated by a distance greater than `prediction_distance`
328 /// or if the given shapes are invalid.
329 ///
330 /// See [`AnyCollider::contact_manifolds_with_context`] for collider types with a non-empty [`AnyCollider::Context`].
331 fn contact_manifolds(
332 &self,
333 other: &Self,
334 position1: RVector,
335 rotation1: impl Into<Rot>,
336 position2: RVector,
337 rotation2: impl Into<Rot>,
338 prediction_distance: f32,
339 manifolds: &mut Vec<ContactManifold>,
340 ) {
341 self.contact_manifolds_with_context(
342 other,
343 position1,
344 rotation1,
345 position2,
346 rotation2,
347 prediction_distance,
348 manifolds,
349 ColliderPairContext::NO_CONTEXT,
350 )
351 }
352
353 /// Returns a conservative minimum thickness used by [Continuous Collision Detection (CCD)][CCD]
354 /// to determine when the collider might tunnel through other geometry.
355 ///
356 /// Typically corresponds to the minimum distance from the centroid
357 /// of any given shape to its surface.
358 ///
359 /// [CCD]: crate::dynamics::ccd
360 fn ccd_thickness(&self) -> f32 {
361 self.ccd_thickness_with_context(ColliderContext::NO_CONTEXT)
362 }
363
364 /// Returns the maximum distance from the collider to the given point.
365 fn max_distance_to_point(&self, point: RVector) -> f32 {
366 self.max_distance_to_point_with_context(point, ColliderContext::NO_CONTEXT)
367 }
368
369 /// Returns the radius of the bounding sphere of the collider.
370 ///
371 /// This is used to compute a size-relative [`ColliderAabbMargin`] for the collider.
372 fn bounding_radius(&self) -> f32 {
373 self.bounding_radius_with_context(ColliderContext::NO_CONTEXT)
374 }
375}
376
377impl<C: AnyCollider<Context = ()>> SimpleCollider for C {}
378
379/// A trait for colliders that support scaling.
380pub trait ScalableCollider: AnyCollider {
381 /// Returns the global scaling factor of the collider.
382 fn scale(&self) -> Vector;
383
384 /// Sets the global scaling factor of the collider.
385 ///
386 /// If the scaling factor is not uniform and the resulting scaled shape
387 /// can not be represented exactly, the given `detail` is used for an approximation.
388 fn set_scale(&mut self, scale: Vector, detail: u32);
389
390 /// Scales the collider by the given scaling factor.
391 ///
392 /// If the scaling factor is not uniform and the resulting scaled shape
393 /// can not be represented exactly, the given `detail` is used for an approximation.
394 fn scale_by(&mut self, factor: Vector, detail: u32) {
395 self.set_scale(factor * self.scale(), detail)
396 }
397}
398
399/// A marker component that indicates that a [collider](Collider) is disabled
400/// and should not detect collisions or be included in spatial queries.
401///
402/// This is useful for temporarily disabling a collider without removing it from the world.
403/// To re-enable the collider, simply remove this component.
404///
405/// Note that a disabled collider will still contribute to the mass properties of the rigid body
406/// it is attached to. Set the [`Mass`] of the collider to zero to prevent this.
407///
408/// [`ColliderDisabled`] only applies to the entity it is attached to, not its children.
409///
410/// # Example
411///
412/// ```
413#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
414#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
415/// # use bevy::prelude::*;
416/// #
417/// #[derive(Component)]
418/// pub struct Character;
419///
420/// /// Disables colliders for all rigid body characters, for example during cutscenes.
421/// fn disable_character_colliders(
422/// mut commands: Commands,
423/// query: Query<Entity, (With<RigidBody>, With<Character>)>,
424/// ) {
425/// for entity in &query {
426/// commands.entity(entity).insert(ColliderDisabled);
427/// }
428/// }
429///
430/// /// Enables colliders for all rigid body characters.
431/// fn enable_character_colliders(
432/// mut commands: Commands,
433/// query: Query<Entity, (With<RigidBody>, With<Character>)>,
434/// ) {
435/// for entity in &query {
436/// commands.entity(entity).remove::<ColliderDisabled>();
437/// }
438/// }
439/// ```
440///
441/// # Related Components
442///
443/// - [`RigidBodyDisabled`]: Disables a rigid body.
444/// - [`JointDisabled`]: Disables a joint constraint.
445#[derive(Reflect, Clone, Copy, Component, Debug, Default)]
446#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
447#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
448#[reflect(Debug, Component, Default)]
449pub struct ColliderDisabled;
450
451/// A component that marks a [`Collider`] as a sensor, also known as a trigger.
452///
453/// Sensor colliders send [collision events](crate::collision#collision-events) and register intersections,
454/// but allow other bodies to pass through them. This is often used to detect when something enters
455/// or leaves an area or is intersecting some shape.
456///
457/// Sensor colliders do *not* contribute to the mass properties of rigid bodies.
458///
459/// # Example
460///
461/// ```
462#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
463#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
464/// use bevy::prelude::*;
465///
466/// fn setup(mut commands: Commands) {
467/// // Spawn a static body with a sensor collider.
468/// // Other bodies will pass through, but it will still send collision events.
469#[cfg_attr(
470 feature = "2d",
471 doc = " commands.spawn((RigidBody::Static, Collider::circle(0.5), Sensor));"
472)]
473#[cfg_attr(
474 feature = "3d",
475 doc = " commands.spawn((RigidBody::Static, Collider::sphere(0.5), Sensor));"
476)]
477/// }
478/// ```
479#[doc(alias = "Trigger")]
480#[derive(Reflect, Clone, Component, Debug, Default, PartialEq, Eq)]
481#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
482#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
483#[reflect(Debug, Component, Default, PartialEq)]
484pub struct Sensor;
485
486/// The Axis-Aligned Bounding Box of a [collider](Collider) in world space.
487///
488/// This is updated automatically.
489///
490/// # Large Worlds
491///
492/// When the `f64` feature is enabled, the AABB still uses
493/// single-precision coordinates for BVH performance reasons,
494/// but rounds outward to ensure that it encompasses the collider.
495/// When the shape is far from the origin, its bounds may become more
496/// conservative and inflated due to the limited precision of `f32`.
497///
498/// At `1e7` units from the origin, the floating-point quantization
499/// is about one unit, while at `1e8` units from the origin, it is
500/// about sixteen units. The simulation should still behave correctly,
501/// but the effectiveness of the broad phase and its acceleration structures
502/// may be reduced, as they will report more false positives. It is recommended
503/// to remain within `1e7` to `1e8` units from the origin to not degrade
504/// broad phase performance too much.
505#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq)]
506#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
507#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
508#[reflect(Debug, Component, PartialEq)]
509pub struct ColliderAabb {
510 /// The minimum point of the AABB.
511 pub min: Vector,
512 /// The maximum point of the AABB.
513 pub max: Vector,
514}
515
516impl Default for ColliderAabb {
517 fn default() -> Self {
518 ColliderAabb::INVALID
519 }
520}
521
522impl ColliderAabb {
523 /// An invalid [`ColliderAabb`] that represents an empty AABB.
524 pub const INVALID: Self = Self {
525 min: Vector::INFINITY,
526 max: Vector::NEG_INFINITY,
527 };
528
529 /// Creates a new [`ColliderAabb`] from the given `center` and `half_size`.
530 pub fn new(center: RVector, half_size: Vector) -> Self {
531 Self {
532 min: next_down_vector(center - half_size.real()),
533 max: next_up_vector(center + half_size.real()),
534 }
535 }
536
537 /// Creates a new [`ColliderAabb`] from its minimum and maximum points.
538 pub fn from_min_max(min: RVector, max: RVector) -> Self {
539 Self {
540 min: next_down_vector(min),
541 max: next_up_vector(max),
542 }
543 }
544
545 /// Creates a new [`ColliderAabb`] from a given `SharedShape`.
546 #[cfg(all(
547 feature = "default-collider",
548 any(feature = "parry-f32", feature = "parry-f64")
549 ))]
550 pub fn from_shape(shape: &crate::parry::shape::SharedShape) -> Self {
551 let aabb = shape.compute_local_aabb();
552 Self {
553 min: next_down_vector(aabb.mins),
554 max: next_up_vector(aabb.maxs),
555 }
556 }
557
558 /// Computes the center of the AABB,
559 #[inline(always)]
560 pub fn center(self) -> RVector {
561 self.min.real().midpoint(self.max.real())
562 }
563
564 /// Computes the size of the AABB.
565 #[inline(always)]
566 pub fn size(self) -> Vector {
567 self.max - self.min
568 }
569
570 /// Computes the half-size of the AABB.
571 #[inline(always)]
572 pub fn half_size(self) -> Vector {
573 self.size() * 0.5
574 }
575
576 /// Merges this AABB with another one.
577 #[inline(always)]
578 pub fn merged(self, other: Self) -> Self {
579 ColliderAabb {
580 min: self.min.min(other.min),
581 max: self.max.max(other.max),
582 }
583 }
584
585 /// Increases the size of the bounding volume in each direction by the given amount.
586 #[inline(always)]
587 pub fn grow(&self, amount: Vector) -> Self {
588 let b = Self {
589 min: self.min - amount,
590 max: self.max + amount,
591 };
592 debug_assert!(b.min.cmple(b.max).all());
593 b
594 }
595
596 /// Decreases the size of the bounding volume in each direction by the given amount.
597 #[inline(always)]
598 pub fn shrink(&self, amount: Vector) -> Self {
599 let b = Self {
600 min: self.min + amount,
601 max: self.max - amount,
602 };
603 debug_assert!(b.min.cmple(b.max).all());
604 b
605 }
606
607 /// Checks if `self` intersects with `other`.
608 #[inline(always)]
609 #[cfg(feature = "2d")]
610 pub fn intersects(&self, other: &Self) -> bool {
611 let x_overlaps = self.min.x <= other.max.x && self.max.x >= other.min.x;
612 let y_overlaps = self.min.y <= other.max.y && self.max.y >= other.min.y;
613 x_overlaps && y_overlaps
614 }
615
616 /// Checks if `self` intersects with `other`.
617 #[inline(always)]
618 #[cfg(feature = "3d")]
619 pub fn intersects(&self, other: &Self) -> bool {
620 let x_overlaps = self.min.x <= other.max.x && self.max.x >= other.min.x;
621 let y_overlaps = self.min.y <= other.max.y && self.max.y >= other.min.y;
622 let z_overlaps = self.min.z <= other.max.z && self.max.z >= other.min.z;
623 x_overlaps && y_overlaps && z_overlaps
624 }
625
626 /// Checks if `self` contains `other`.
627 #[inline(always)]
628 pub fn contains(&self, other: &Self) -> bool {
629 self.min.cmple(other.min).all() && self.max.cmpge(other.max).all()
630 }
631}
632
633impl From<ColliderAabb> for obvhs::aabb::Aabb {
634 fn from(value: ColliderAabb) -> Self {
635 Self {
636 #[cfg(feature = "2d")]
637 min: value.min.extend(-0.5).to_array().into(),
638 #[cfg(feature = "2d")]
639 max: value.max.extend(0.5).to_array().into(),
640 #[cfg(feature = "3d")]
641 min: value.min.to_array().into(),
642 #[cfg(feature = "3d")]
643 max: value.max.to_array().into(),
644 }
645 }
646}
647
648/// An Axis-Aligned Bounding Box that contains the [`ColliderAabb`] with an additional margin.
649///
650/// This is used to avoid updating the [`ColliderTree`] every time a collider moves only a small amount.
651///
652/// The enlarged AABB is updated automatically whenever the [`ColliderAabb`]
653/// moves beyond the bounds of the current enlarged AABB.
654///
655/// [`ColliderTree`]: crate::collider_tree::ColliderTree
656#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, PartialEq)]
657#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
658#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
659#[reflect(Debug, Component, PartialEq)]
660pub struct EnlargedAabb(ColliderAabb);
661
662impl EnlargedAabb {
663 /// Creates a new [`EnlargedAabb`] from the given [`ColliderAabb`].
664 pub fn new(aabb: ColliderAabb) -> Self {
665 Self(aabb)
666 }
667
668 /// Updates the enlarged AABB with the given [`ColliderAabb`] and margin.
669 ///
670 /// If the AABB is already contained within the enlarged AABB, nothing happens.
671 ///
672 /// Returns `true` if the AABB was updated.
673 pub fn update(&mut self, aabb: &ColliderAabb, margin: f32) -> bool {
674 if self.contains(aabb) {
675 return false;
676 }
677
678 let margin = Vector::splat(margin);
679 self.0.min = aabb.min - margin;
680 self.0.max = aabb.max + margin;
681
682 true
683 }
684
685 /// Gets the [`ColliderAabb`] of the enlarged AABB.
686 pub fn get(&self) -> ColliderAabb {
687 self.0
688 }
689}
690
691/// The margin used to enlarge the [`ColliderAabb`] of a collider into its [`EnlargedAabb`].
692///
693/// Enlarging the AABB allows the collider to move by a small amount without triggering
694/// an update of the [`ColliderTree`], which improves performance.
695///
696/// The margin is computed automatically from the size of the collider, scaled relative to the
697/// [`PhysicsLengthUnit`]. Larger shapes get a larger margin (up to [`ColliderAabbMargin::MAX`]),
698/// while small shapes get a margin proportional to their size to avoid wastefully fat AABBs.
699///
700/// [`ColliderTree`]: crate::collider_tree::ColliderTree
701/// [`PhysicsLengthUnit`]: crate::dynamics::solver::PhysicsLengthUnit
702#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, PartialEq)]
703#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
704#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
705#[reflect(Debug, Component, Default, PartialEq)]
706pub struct ColliderAabbMargin(pub f32);
707
708impl ColliderAabbMargin {
709 /// The maximum AABB margin before being scaled by the [`PhysicsLengthUnit`].
710 ///
711 /// This is used to fatten AABBs in the broad phase acceleration structure. This allows proxies
712 /// to move by a small amount without triggering a tree adjustment.
713 ///
714 /// [`PhysicsLengthUnit`]: crate::dynamics::solver::PhysicsLengthUnit
715 pub const MAX: f32 = 0.05;
716
717 /// For small shapes, the margin is limited to this fraction of the shape's bounding radius.
718 pub const FRACTION: f32 = 0.125;
719
720 /// Computes the AABB margin for a collider with the given `bounding_radius` and [`PhysicsLengthUnit`].
721 ///
722 /// The margin is clamped to at most [`ColliderAabbMargin::MAX`] scaled by the length unit,
723 /// and at most [`ColliderAabbMargin::FRACTION`] of the bounding radius for small shapes.
724 ///
725 /// [`PhysicsLengthUnit`]: crate::dynamics::solver::PhysicsLengthUnit
726 #[inline]
727 pub fn from_bounding_radius(bounding_radius: f32, length_unit: f32) -> Self {
728 Self((length_unit * Self::MAX).min(Self::FRACTION * bounding_radius))
729 }
730}
731
732/// A component that adds an extra margin or "skin" around [`Collider`] shapes to help maintain
733/// additional separation to other objects. This added thickness can help improve
734/// stability and performance in some cases, especially for thin shapes such as trimeshes.
735///
736/// There are three primary reasons for collision margins:
737///
738/// 1. Collision detection is often more efficient when shapes are not overlapping
739/// further than their collision margins. Deeply overlapping shapes require
740/// more expensive collision algorithms.
741///
742/// 2. Some shapes such as triangles and planes are infinitely thin,
743/// which can cause precision errors. A collision margin adds artificial
744/// thickness to shapes, improving stability.
745///
746/// 3. Overall, collision margins give the physics engine more
747/// room for error when resolving contacts. This can also help
748/// prevent visible artifacts such as objects poking through the ground.
749///
750/// If a rigid body with a [`CollisionMargin`] has colliders as child entities,
751/// and those colliders don't have their own [`CollisionMargin`] components,
752/// the colliders will use the rigid body's [`CollisionMargin`].
753///
754/// # Example
755///
756/// ```
757#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
758#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
759/// use bevy::prelude::*;
760///
761/// fn setup(mut commands: Commands) {
762#[cfg_attr(
763 feature = "2d",
764 doc = " // Spawn a rigid body with a collider.
765 // A margin of `0.1` is added around the shape.
766 commands.spawn((
767 RigidBody::Dynamic,
768 Collider::capsule(2.0, 0.5),
769 CollisionMargin(0.1),
770 ));"
771)]
772#[cfg_attr(
773 feature = "3d",
774 doc = " let mesh = Mesh::from(Torus::default());
775
776 // Spawn a rigid body with a triangle mesh collider.
777 // A margin of `0.1` is added around the shape.
778 commands.spawn((
779 RigidBody::Dynamic,
780 Collider::trimesh_from_mesh(&mesh).unwrap(),
781 CollisionMargin(0.1),
782 ));"
783)]
784/// }
785/// ```
786#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
787#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
788#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
789#[reflect(Component)]
790#[doc(alias = "ContactSkin")]
791pub struct CollisionMargin(pub f32);
792
793/// A component for reading which entities are colliding with a collider entity.
794/// Must be added manually for desired colliders.
795///
796/// # Example
797///
798/// ```
799#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
800#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
801/// use bevy::prelude::*;
802///
803/// fn setup(mut commands: Commands) {
804/// commands.spawn((
805/// RigidBody::Dynamic,
806/// Collider::capsule(0.5, 1.5),
807/// // Add the `CollidingEntities` component to read entities colliding with this entity.
808/// CollidingEntities::default(),
809/// ));
810/// }
811///
812/// fn my_system(query: Query<(Entity, &CollidingEntities)>) {
813/// for (entity, colliding_entities) in &query {
814/// println!(
815/// "{} is colliding with the following entities: {:?}",
816/// entity,
817/// colliding_entities,
818/// );
819/// }
820/// }
821/// ```
822#[derive(Reflect, Clone, Component, Debug, Default, Deref, DerefMut, PartialEq, Eq)]
823#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
824#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
825#[reflect(Debug, Component, Default, PartialEq)]
826pub struct CollidingEntities(pub EntityHashSet);
827
828impl MapEntities for CollidingEntities {
829 fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
830 self.0 = self
831 .0
832 .clone()
833 .into_iter()
834 .map(|e| entity_mapper.get_mapped(e))
835 .collect()
836 }
837}