use crate::prelude::*;
use bevy::{
ecs::entity::{EntityMapper, MapEntities},
prelude::*,
};
use parry::query::{details::TOICompositeShapeShapeBestFirstVisitor, ShapeCastOptions};
#[cfg_attr(feature = "2d", doc = " Collider::circle(0.5),")]
#[cfg_attr(feature = "3d", doc = " Collider::sphere(0.5),")]
#[derive(Component)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct ShapeCaster {
pub enabled: bool,
pub shape: Collider,
pub origin: Vector,
global_origin: Vector,
#[cfg(feature = "2d")]
pub shape_rotation: Scalar,
#[cfg(feature = "3d")]
pub shape_rotation: Quaternion,
#[cfg(feature = "2d")]
global_shape_rotation: Scalar,
#[cfg(feature = "3d")]
global_shape_rotation: Quaternion,
pub direction: Dir,
global_direction: Dir,
pub max_time_of_impact: Scalar,
pub max_hits: u32,
pub ignore_origin_penetration: bool,
pub ignore_self: bool,
pub query_filter: SpatialQueryFilter,
}
impl Default for ShapeCaster {
fn default() -> Self {
Self {
enabled: true,
#[cfg(feature = "2d")]
shape: Collider::circle(0.0),
#[cfg(feature = "3d")]
shape: Collider::sphere(0.0),
origin: Vector::ZERO,
global_origin: Vector::ZERO,
#[cfg(feature = "2d")]
shape_rotation: 0.0,
#[cfg(feature = "3d")]
shape_rotation: Quaternion::IDENTITY,
#[cfg(feature = "2d")]
global_shape_rotation: 0.0,
#[cfg(feature = "3d")]
global_shape_rotation: Quaternion::IDENTITY,
direction: Dir::X,
global_direction: Dir::X,
max_time_of_impact: Scalar::MAX,
max_hits: 1,
ignore_origin_penetration: false,
ignore_self: true,
query_filter: SpatialQueryFilter::default(),
}
}
}
impl ShapeCaster {
#[cfg(feature = "2d")]
pub fn new(
shape: impl Into<Collider>,
origin: Vector,
shape_rotation: Scalar,
direction: Dir,
) -> Self {
Self {
shape: shape.into(),
origin,
shape_rotation,
direction,
..default()
}
}
#[cfg(feature = "3d")]
pub fn new(
shape: impl Into<Collider>,
origin: Vector,
shape_rotation: Quaternion,
direction: Dir,
) -> Self {
Self {
shape: shape.into(),
origin,
shape_rotation,
direction,
..default()
}
}
pub fn with_origin(mut self, origin: Vector) -> Self {
self.origin = origin;
self
}
pub fn with_direction(mut self, direction: Dir) -> Self {
self.direction = direction;
self
}
pub fn with_ignore_origin_penetration(mut self, ignore: bool) -> Self {
self.ignore_origin_penetration = ignore;
self
}
pub fn with_ignore_self(mut self, ignore: bool) -> Self {
self.ignore_self = ignore;
self
}
pub fn with_max_time_of_impact(mut self, max_time_of_impact: Scalar) -> Self {
self.max_time_of_impact = max_time_of_impact;
self
}
pub fn with_max_hits(mut self, max_hits: u32) -> Self {
self.max_hits = max_hits;
self
}
pub fn with_query_filter(mut self, query_filter: SpatialQueryFilter) -> Self {
self.query_filter = query_filter;
self
}
pub fn enable(&mut self) {
self.enabled = true;
}
pub fn disable(&mut self) {
self.enabled = false;
}
pub fn global_origin(&self) -> Vector {
self.global_origin
}
#[cfg(feature = "2d")]
pub fn global_shape_rotation(&self) -> Scalar {
self.global_shape_rotation
}
#[cfg(feature = "3d")]
pub fn global_shape_rotation(&self) -> Quaternion {
self.global_shape_rotation
}
pub fn global_direction(&self) -> Dir {
self.global_direction
}
pub(crate) fn set_global_origin(&mut self, global_origin: Vector) {
self.global_origin = global_origin;
}
#[cfg(feature = "2d")]
pub(crate) fn set_global_shape_rotation(&mut self, global_rotation: Scalar) {
self.global_shape_rotation = global_rotation;
}
#[cfg(feature = "3d")]
pub(crate) fn set_global_shape_rotation(&mut self, global_rotation: Quaternion) {
self.global_shape_rotation = global_rotation;
}
pub(crate) fn set_global_direction(&mut self, global_direction: Dir) {
self.global_direction = global_direction;
}
pub(crate) fn cast(
&self,
caster_entity: Entity,
hits: &mut ShapeHits,
query_pipeline: &SpatialQueryPipeline,
) {
let mut query_filter = self.query_filter.clone();
if self.ignore_self {
query_filter.excluded_entities.insert(caster_entity);
}
hits.count = 0;
let shape_rotation: Rotation;
#[cfg(feature = "2d")]
{
shape_rotation = Rotation::from_radians(self.global_shape_rotation());
}
#[cfg(feature = "3d")]
{
shape_rotation = Rotation::from(self.global_shape_rotation());
}
let shape_isometry = utils::make_isometry(self.global_origin(), shape_rotation);
let shape_direction = self.global_direction().adjust_precision().into();
while hits.count < self.max_hits {
let pipeline_shape = query_pipeline.as_composite_shape(query_filter.clone());
let mut visitor = TOICompositeShapeShapeBestFirstVisitor::new(
&*query_pipeline.dispatcher,
&shape_isometry,
&shape_direction,
&pipeline_shape,
&**self.shape.shape_scaled(),
ShapeCastOptions {
max_time_of_impact: self.max_time_of_impact,
stop_at_penetration: !self.ignore_origin_penetration,
..default()
},
);
if let Some(hit) = query_pipeline.qbvh.traverse_best_first(&mut visitor).map(
|(_, (entity_index, hit))| ShapeHitData {
entity: query_pipeline.entity_from_index(entity_index),
time_of_impact: hit.time_of_impact,
point1: hit.witness1.into(),
point2: hit.witness2.into(),
normal1: hit.normal1.into(),
normal2: hit.normal2.into(),
},
) {
if (hits.vector.len() as u32) < hits.count + 1 {
hits.vector.push(hit);
} else {
hits.vector[hits.count as usize] = hit;
}
hits.count += 1;
query_filter.excluded_entities.insert(hit.entity);
} else {
return;
}
}
}
}
#[derive(Component, Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct ShapeHits {
pub(crate) vector: Vec<ShapeHitData>,
pub(crate) count: u32,
}
impl ShapeHits {
pub fn as_slice(&self) -> &[ShapeHitData] {
&self.vector[0..self.count as usize]
}
#[doc(alias = "count")]
pub fn len(&self) -> usize {
self.count as usize
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
pub fn clear(&mut self) {
self.vector.clear();
self.count = 0;
}
pub fn iter(&self) -> std::slice::Iter<ShapeHitData> {
self.as_slice().iter()
}
}
impl MapEntities for ShapeHits {
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
for hit in &mut self.vector {
hit.map_entities(entity_mapper);
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct ShapeHitData {
pub entity: Entity,
pub time_of_impact: Scalar,
pub point1: Vector,
pub point2: Vector,
pub normal1: Vector,
pub normal2: Vector,
}
impl MapEntities for ShapeHitData {
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
self.entity = entity_mapper.map_entity(self.entity);
}
}