use crate::prelude::*;
#[cfg(all(feature = "3d", feature = "async-collider"))]
use bevy::utils::HashMap;
use bevy::{
ecs::entity::{EntityMapper, MapEntities},
prelude::*,
utils::HashSet,
};
#[cfg(feature = "default-collider")]
mod parry;
#[cfg(feature = "default-collider")]
pub use parry::*;
pub trait IntoCollider<C: AnyCollider> {
fn collider(&self) -> C;
}
pub trait AnyCollider: Component {
#[cfg_attr(
feature = "2d",
doc = "\n\nThe rotation is counterclockwise and in radians."
)]
fn aabb(&self, position: Vector, rotation: impl Into<Rotation>) -> ColliderAabb;
#[cfg_attr(
feature = "2d",
doc = "\n\nThe rotation is counterclockwise and in radians."
)]
fn swept_aabb(
&self,
start_position: Vector,
start_rotation: impl Into<Rotation>,
end_position: Vector,
end_rotation: impl Into<Rotation>,
) -> ColliderAabb {
self.aabb(start_position, start_rotation)
.merged(self.aabb(end_position, end_rotation))
}
fn mass_properties(&self, density: Scalar) -> ColliderMassProperties;
fn contact_manifolds(
&self,
other: &Self,
position1: Vector,
rotation1: impl Into<Rotation>,
position2: Vector,
rotation2: impl Into<Rotation>,
prediction_distance: Scalar,
) -> Vec<ContactManifold>;
}
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(all(feature = "3d", feature = "async-collider"))]
#[derive(Component, Clone, Debug, Default, Deref, DerefMut)]
pub struct AsyncCollider(pub ComputedCollider);
#[cfg(all(feature = "3d", feature = "async-collider"))]
#[derive(Component, Clone, Debug, Default, PartialEq)]
pub struct AsyncSceneCollider {
pub default_shape: Option<ComputedCollider>,
pub meshes_by_name: HashMap<String, Option<AsyncSceneColliderData>>,
}
#[cfg(all(feature = "3d", feature = "async-collider"))]
impl AsyncSceneCollider {
pub fn new(default_shape: Option<ComputedCollider>) -> Self {
Self {
default_shape,
meshes_by_name: default(),
}
}
pub fn with_shape_for_name(mut self, name: &str, shape: ComputedCollider) -> Self {
if let Some(Some(data)) = self.meshes_by_name.get_mut(name) {
data.shape = shape;
} else {
self.meshes_by_name.insert(
name.to_string(),
Some(AsyncSceneColliderData { shape, ..default() }),
);
}
self
}
pub fn with_layers_for_name(mut self, name: &str, layers: CollisionLayers) -> Self {
if let Some(Some(data)) = self.meshes_by_name.get_mut(name) {
data.layers = layers;
} else {
self.meshes_by_name.insert(
name.to_string(),
Some(AsyncSceneColliderData {
layers,
..default()
}),
);
}
self
}
pub fn with_density_for_name(mut self, name: &str, density: Scalar) -> Self {
if let Some(Some(data)) = self.meshes_by_name.get_mut(name) {
data.density = density;
} else {
self.meshes_by_name.insert(
name.to_string(),
Some(AsyncSceneColliderData {
density,
..default()
}),
);
}
self
}
pub fn without_shape_with_name(mut self, name: &str) -> Self {
self.meshes_by_name.insert(name.to_string(), None);
self
}
}
#[cfg(all(feature = "3d", feature = "async-collider"))]
#[derive(Clone, Debug, PartialEq)]
pub struct AsyncSceneColliderData {
pub shape: ComputedCollider,
pub layers: CollisionLayers,
pub density: Scalar,
}
#[cfg(all(feature = "3d", feature = "async-collider"))]
impl Default for AsyncSceneColliderData {
fn default() -> Self {
Self {
shape: ComputedCollider::TriMesh,
layers: CollisionLayers::default(),
density: 1.0,
}
}
}
#[cfg(all(feature = "3d", feature = "collider-from-mesh"))]
#[derive(Component, Clone, Debug, Default, PartialEq)]
pub enum ComputedCollider {
#[default]
TriMesh,
#[cfg(feature = "default-collider")]
TriMeshWithFlags(TriMeshFlags),
ConvexHull,
#[cfg(feature = "default-collider")]
ConvexDecomposition(VHACDParameters),
}
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct ColliderParent(pub(crate) Entity);
impl ColliderParent {
pub const fn get(&self) -> Entity {
self.0
}
}
impl MapEntities for ColliderParent {
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
self.0 = entity_mapper.map_entity(self.0)
}
}
#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct ColliderTransform {
pub translation: Vector,
pub rotation: Rotation,
pub scale: Vector,
}
impl ColliderTransform {
pub fn transform_point(&self, mut point: Vector) -> Vector {
point *= self.scale;
point = self.rotation.rotate(point);
point += self.translation;
point
}
}
impl Default for ColliderTransform {
fn default() -> Self {
Self {
translation: Vector::ZERO,
rotation: Rotation::default(),
scale: Vector::ONE,
}
}
}
impl From<Transform> for ColliderTransform {
fn from(value: Transform) -> Self {
Self {
#[cfg(feature = "2d")]
translation: value.translation.truncate().adjust_precision(),
#[cfg(feature = "3d")]
translation: value.translation.adjust_precision(),
rotation: Rotation::from(value.rotation.adjust_precision()),
#[cfg(feature = "2d")]
scale: value.scale.truncate().adjust_precision(),
#[cfg(feature = "3d")]
scale: value.scale.adjust_precision(),
}
}
}
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[doc(alias = "Trigger")]
#[derive(Reflect, Clone, Component, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct Sensor;
#[derive(Clone, Copy, Component, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct ColliderAabb {
pub min: Vector,
pub max: Vector,
}
impl ColliderAabb {
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(feature = "default-collider")]
pub fn from_shape(shape: &crate::parry::shape::SharedShape) -> Self {
let aabb = shape.compute_local_aabb();
Self {
min: aabb.mins.into(),
max: aabb.maxs.into(),
}
}
pub fn center(self) -> Vector {
(self.min + self.max) / 2.0
}
pub fn size(self) -> Vector {
self.max - self.min
}
pub fn merged(self, other: Self) -> Self {
ColliderAabb {
min: self.min.min(other.min),
max: self.max.max(other.max),
}
}
#[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
}
}
impl Default for ColliderAabb {
fn default() -> Self {
ColliderAabb {
min: Vector::INFINITY,
max: Vector::NEG_INFINITY,
}
}
}
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[derive(Reflect, Clone, Component, Debug, Default, Deref, DerefMut, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct CollidingEntities(pub HashSet<Entity>);
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.map_entity(e))
.collect()
}
}