use crate::prelude::*;
use bevy::{
ecs::entity::{EntityMapper, MapEntities},
prelude::*,
};
use parry::query::{
details::RayCompositeShapeToiAndNormalBestFirstVisitor, visitors::RayIntersectionsVisitor,
};
#[derive(Component)]
pub struct RayCaster {
pub enabled: bool,
pub origin: Vector,
global_origin: Vector,
pub direction: Vector,
global_direction: Vector,
pub max_time_of_impact: Scalar,
pub max_hits: u32,
pub solid: bool,
pub ignore_self: bool,
pub query_filter: SpatialQueryFilter,
}
impl Default for RayCaster {
fn default() -> Self {
Self {
enabled: true,
origin: Vector::ZERO,
global_origin: Vector::ZERO,
direction: Vector::ZERO,
global_direction: Vector::ZERO,
max_time_of_impact: Scalar::MAX,
max_hits: u32::MAX,
solid: true,
ignore_self: true,
query_filter: SpatialQueryFilter::default(),
}
}
}
impl RayCaster {
pub fn new(origin: Vector, direction: Vector) -> Self {
Self {
origin,
direction,
..default()
}
}
pub fn with_origin(mut self, origin: Vector) -> Self {
self.origin = origin;
self
}
pub fn with_direction(mut self, direction: Vector) -> Self {
self.direction = direction;
self
}
pub fn with_solidness(mut self, solid: bool) -> Self {
self.solid = solid;
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
}
pub fn global_direction(&self) -> Vector {
self.global_direction
}
pub(crate) fn set_global_origin(&mut self, global_origin: Vector) {
self.global_origin = global_origin;
}
pub(crate) fn set_global_direction(&mut self, global_direction: Vector) {
self.global_direction = global_direction;
}
pub(crate) fn cast(
&self,
caster_entity: Entity,
hits: &mut RayHits,
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;
if self.max_hits == 1 {
let pipeline_shape = query_pipeline.as_composite_shape(query_filter);
let ray =
parry::query::Ray::new(self.global_origin().into(), self.global_direction().into());
let mut visitor = RayCompositeShapeToiAndNormalBestFirstVisitor::new(
&pipeline_shape,
&ray,
self.max_time_of_impact,
self.solid,
);
if let Some(hit) = query_pipeline.qbvh.traverse_best_first(&mut visitor).map(
|(_, (entity_index, hit))| RayHitData {
entity: query_pipeline.entity_from_index(entity_index),
time_of_impact: hit.toi,
normal: hit.normal.into(),
},
) {
if (hits.vector.len() as u32) < hits.count + 1 {
hits.vector.push(hit);
} else {
hits.vector[0] = hit;
}
hits.count = 1;
}
} else {
let ray =
parry::query::Ray::new(self.global_origin().into(), self.global_direction().into());
let mut leaf_callback = &mut |entity_index: &u32| {
let entity = query_pipeline.entity_from_index(*entity_index);
if let Some((iso, shape, layers)) = query_pipeline.colliders.get(&entity) {
if query_filter.test(entity, *layers) {
if let Some(hit) = shape.shape_scaled().cast_ray_and_get_normal(
iso,
&ray,
self.max_time_of_impact,
self.solid,
) {
if (hits.vector.len() as u32) < hits.count + 1 {
hits.vector.push(RayHitData {
entity,
time_of_impact: hit.toi,
normal: hit.normal.into(),
});
} else {
hits.vector[hits.count as usize] = RayHitData {
entity,
time_of_impact: hit.toi,
normal: hit.normal.into(),
};
}
hits.count += 1;
return hits.count < self.max_hits;
}
}
}
true
};
let mut visitor =
RayIntersectionsVisitor::new(&ray, self.max_time_of_impact, &mut leaf_callback);
query_pipeline.qbvh.traverse_depth_first(&mut visitor);
}
}
}
#[derive(Component, Clone, Default)]
pub struct RayHits {
pub(crate) vector: Vec<RayHitData>,
pub(crate) count: u32,
}
impl RayHits {
pub fn as_slice(&self) -> &[RayHitData] {
&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<RayHitData> {
self.as_slice().iter()
}
pub fn iter_sorted(&self) -> std::vec::IntoIter<RayHitData> {
let mut vector = self.as_slice().to_vec();
vector.sort_by(|a, b| a.time_of_impact.partial_cmp(&b.time_of_impact).unwrap());
vector.into_iter()
}
}
impl MapEntities for RayHits {
fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
for hit in &mut self.vector {
hit.map_entities(entity_mapper);
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RayHitData {
pub entity: Entity,
pub time_of_impact: Scalar,
pub normal: Vector,
}
impl MapEntities for RayHitData {
fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
self.entity = entity_mapper.get_or_reserve(self.entity);
}
}