use crate::prelude::*;
use bevy::{
ecs::{
component::Mutable,
entity::{EntityMapper, MapEntities, hash_set::EntityHashSet},
system::{ReadOnlySystemParam, SystemParam, SystemParamItem},
},
prelude::*,
};
use derive_more::From;
mod backend;
pub use backend::{ColliderBackendPlugin, ColliderMarker};
#[cfg(all(feature = "collider-from-mesh", feature = "default-collider"))]
mod cache;
#[cfg(all(feature = "collider-from-mesh", feature = "default-collider"))]
pub use cache::ColliderCachePlugin;
pub mod collider_hierarchy;
pub mod collider_transform;
#[cfg(all(feature = "3d", any(feature = "parry-f32", feature = "parry-f64")))]
pub mod trimesh_builder;
mod layers;
pub use layers::*;
#[cfg(all(
feature = "default-collider",
any(feature = "parry-f32", feature = "parry-f64")
))]
mod parry;
#[cfg(all(
feature = "default-collider",
any(feature = "parry-f32", feature = "parry-f64")
))]
pub use parry::*;
#[cfg(feature = "default-collider")]
mod constructor;
#[cfg(feature = "default-collider")]
pub use constructor::{
ColliderConstructor, ColliderConstructorHierarchy, ColliderConstructorHierarchyConfig,
ColliderConstructorHierarchyReady, ColliderConstructorReady,
};
pub trait IntoCollider<C: AnyCollider> {
fn collider(&self) -> C;
}
#[derive(Deref)]
pub struct AabbContext<'a, 'w, 's, T: ReadOnlySystemParam> {
pub entity: Entity,
#[deref]
item: &'a SystemParamItem<'w, 's, T>,
}
impl<T: ReadOnlySystemParam> Clone for AabbContext<'_, '_, '_, T> {
fn clone(&self) -> Self {
Self {
entity: self.entity,
item: self.item,
}
}
}
impl<'a, 'w, 's, T: ReadOnlySystemParam> AabbContext<'a, 'w, 's, T> {
pub fn new(entity: Entity, item: &'a <T as SystemParam>::Item<'w, 's>) -> Self {
Self { entity, item }
}
}
impl AabbContext<'_, '_, '_, ()> {
fn fake() -> Self {
Self {
entity: Entity::PLACEHOLDER,
item: &(),
}
}
}
#[derive(Deref)]
pub struct ContactManifoldContext<'a, 'w, 's, T: ReadOnlySystemParam> {
pub entity1: Entity,
pub entity2: Entity,
#[deref]
item: &'a SystemParamItem<'w, 's, T>,
}
impl<'a, 'w, 's, T: ReadOnlySystemParam> ContactManifoldContext<'a, 'w, 's, T> {
pub fn new(
entity1: Entity,
entity2: Entity,
item: &'a <T as SystemParam>::Item<'w, 's>,
) -> Self {
Self {
entity1,
entity2,
item,
}
}
}
impl ContactManifoldContext<'_, '_, '_, ()> {
fn fake() -> Self {
Self {
entity1: Entity::PLACEHOLDER,
entity2: Entity::PLACEHOLDER,
item: &(),
}
}
}
pub trait AnyCollider: Component<Mutability = Mutable> + ComputeMassProperties {
#[cfg_attr(
feature = "2d",
doc = "# use avian2d::{prelude::*, math::{Vector, Scalar}};"
)]
#[cfg_attr(
feature = "3d",
doc = "# use avian3d::{prelude::*, math::{Vector, Scalar}};"
)]
type Context: for<'w, 's> ReadOnlySystemParam<Item<'w, 's>: Send + Sync>;
#[cfg_attr(
feature = "2d",
doc = "\n\nThe rotation is counterclockwise and in radians."
)]
fn aabb_with_context(
&self,
position: Vector,
rotation: impl Into<Rotation>,
context: AabbContext<Self::Context>,
) -> ColliderAabb;
#[cfg_attr(
feature = "2d",
doc = "\n\nThe rotation is counterclockwise and in radians."
)]
fn swept_aabb_with_context(
&self,
start_position: Vector,
start_rotation: impl Into<Rotation>,
end_position: Vector,
end_rotation: impl Into<Rotation>,
context: AabbContext<Self::Context>,
) -> ColliderAabb {
self.aabb_with_context(start_position, start_rotation, context.clone())
.merged(self.aabb_with_context(end_position, end_rotation, context))
}
fn contact_manifolds_with_context(
&self,
other: &Self,
position1: Vector,
rotation1: impl Into<Rotation>,
position2: Vector,
rotation2: impl Into<Rotation>,
prediction_distance: Scalar,
manifolds: &mut Vec<ContactManifold>,
context: ContactManifoldContext<Self::Context>,
);
}
pub trait SimpleCollider: AnyCollider<Context = ()> {
fn aabb(&self, position: Vector, rotation: impl Into<Rotation>) -> ColliderAabb {
self.aabb_with_context(position, rotation, AabbContext::fake())
}
fn swept_aabb(
&self,
start_position: Vector,
start_rotation: impl Into<Rotation>,
end_position: Vector,
end_rotation: impl Into<Rotation>,
) -> ColliderAabb {
self.swept_aabb_with_context(
start_position,
start_rotation,
end_position,
end_rotation,
AabbContext::fake(),
)
}
fn contact_manifolds(
&self,
other: &Self,
position1: Vector,
rotation1: impl Into<Rotation>,
position2: Vector,
rotation2: impl Into<Rotation>,
prediction_distance: Scalar,
manifolds: &mut Vec<ContactManifold>,
) {
self.contact_manifolds_with_context(
other,
position1,
rotation1,
position2,
rotation2,
prediction_distance,
manifolds,
ContactManifoldContext::fake(),
)
}
}
impl<C: AnyCollider<Context = ()>> SimpleCollider for C {}
pub trait ScalableCollider: AnyCollider {
fn scale(&self) -> Vector;
fn set_scale(&mut self, scale: Vector, detail: u32);
fn scale_by(&mut self, factor: Vector, detail: u32) {
self.set_scale(factor * self.scale(), detail)
}
}
#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
#[derive(Reflect, Clone, Copy, Component, Debug, Default)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, Component, Default)]
pub struct ColliderDisabled;
#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
#[cfg_attr(
feature = "2d",
doc = " commands.spawn((RigidBody::Static, Collider::circle(0.5), Sensor));"
)]
#[cfg_attr(
feature = "3d",
doc = " commands.spawn((RigidBody::Static, Collider::sphere(0.5), Sensor));"
)]
#[doc(alias = "Trigger")]
#[derive(Reflect, Clone, Component, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, Component, Default, PartialEq)]
pub struct Sensor;
#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, Component, PartialEq)]
pub struct ColliderAabb {
pub min: Vector,
pub max: Vector,
}
impl Default for ColliderAabb {
fn default() -> Self {
ColliderAabb::INVALID
}
}
impl ColliderAabb {
pub const INVALID: Self = Self {
min: Vector::INFINITY,
max: Vector::NEG_INFINITY,
};
pub fn new(center: Vector, half_size: Vector) -> Self {
Self {
min: center - half_size,
max: center + half_size,
}
}
pub fn from_min_max(min: Vector, max: Vector) -> Self {
Self { min, max }
}
#[cfg(all(
feature = "default-collider",
any(feature = "parry-f32", feature = "parry-f64")
))]
pub fn from_shape(shape: &crate::parry::shape::SharedShape) -> Self {
let aabb = shape.compute_local_aabb();
Self {
min: aabb.mins,
max: aabb.maxs,
}
}
#[inline(always)]
pub fn center(self) -> Vector {
self.min.midpoint(self.max)
}
#[inline(always)]
pub fn size(self) -> Vector {
self.max - self.min
}
#[inline(always)]
pub fn merged(self, other: Self) -> Self {
ColliderAabb {
min: self.min.min(other.min),
max: self.max.max(other.max),
}
}
#[inline(always)]
pub fn grow(&self, amount: Vector) -> Self {
let b = Self {
min: self.min - amount,
max: self.max + amount,
};
debug_assert!(b.min.cmple(b.max).all());
b
}
#[inline(always)]
pub fn shrink(&self, amount: Vector) -> Self {
let b = Self {
min: self.min + amount,
max: self.max - amount,
};
debug_assert!(b.min.cmple(b.max).all());
b
}
#[inline(always)]
#[cfg(feature = "2d")]
pub fn intersects(&self, other: &Self) -> bool {
let x_overlaps = self.min.x <= other.max.x && self.max.x >= other.min.x;
let y_overlaps = self.min.y <= other.max.y && self.max.y >= other.min.y;
x_overlaps && y_overlaps
}
#[inline(always)]
#[cfg(feature = "3d")]
pub fn intersects(&self, other: &Self) -> bool {
let x_overlaps = self.min.x <= other.max.x && self.max.x >= other.min.x;
let y_overlaps = self.min.y <= other.max.y && self.max.y >= other.min.y;
let z_overlaps = self.min.z <= other.max.z && self.max.z >= other.min.z;
x_overlaps && y_overlaps && z_overlaps
}
#[inline(always)]
pub fn contains(&self, other: &Self) -> bool {
self.min.cmple(other.min).all() && self.max.cmpge(other.max).all()
}
}
impl From<ColliderAabb> for obvhs::aabb::Aabb {
fn from(value: ColliderAabb) -> Self {
Self {
#[cfg(feature = "2d")]
min: value.min.f32().extend(-0.5).to_array().into(),
#[cfg(feature = "2d")]
max: value.max.f32().extend(0.5).to_array().into(),
#[cfg(feature = "3d")]
min: value.min.f32().to_array().into(),
#[cfg(feature = "3d")]
max: value.max.f32().to_array().into(),
}
}
}
#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, Component, PartialEq)]
pub struct EnlargedAabb(ColliderAabb);
impl EnlargedAabb {
pub fn new(aabb: ColliderAabb) -> Self {
Self(aabb)
}
pub fn update(&mut self, aabb: &ColliderAabb, margin: Scalar) -> bool {
if self.contains(aabb) {
return false;
}
let margin = Vector::splat(margin);
self.0.min = aabb.min - margin;
self.0.max = aabb.max + margin;
true
}
pub fn get(&self) -> ColliderAabb {
self.0
}
}
#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
#[cfg_attr(
feature = "2d",
doc = " // Spawn a rigid body with a collider.
// A margin of `0.1` is added around the shape.
commands.spawn((
RigidBody::Dynamic,
Collider::capsule(2.0, 0.5),
CollisionMargin(0.1),
));"
)]
#[cfg_attr(
feature = "3d",
doc = " let mesh = Mesh::from(Torus::default());
// Spawn a rigid body with a triangle mesh collider.
// A margin of `0.1` is added around the shape.
commands.spawn((
RigidBody::Dynamic,
Collider::trimesh_from_mesh(&mesh).unwrap(),
CollisionMargin(0.1),
));"
)]
#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Component)]
#[doc(alias = "ContactSkin")]
pub struct CollisionMargin(pub Scalar);
#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
#[derive(Reflect, Clone, Component, Debug, Default, Deref, DerefMut, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, Component, Default, PartialEq)]
pub struct CollidingEntities(pub EntityHashSet);
impl MapEntities for CollidingEntities {
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
self.0 = self
.0
.clone()
.into_iter()
.map(|e| entity_mapper.get_mapped(e))
.collect()
}
}