mod query_filter;
mod ray_caster;
#[cfg(any(feature = "parry-f32", feature = "parry-f64"))]
mod shape_caster;
#[cfg(any(feature = "parry-f32", feature = "parry-f64"))]
mod system_param;
mod diagnostics;
pub use diagnostics::SpatialQueryDiagnostics;
pub use query_filter::*;
pub use ray_caster::*;
#[cfg(any(feature = "parry-f32", feature = "parry-f64"))]
pub use shape_caster::*;
#[cfg(any(feature = "parry-f32", feature = "parry-f64"))]
pub use system_param::*;
use crate::prelude::*;
use bevy::prelude::*;
pub struct SpatialQueryPlugin;
impl Plugin for SpatialQueryPlugin {
fn build(&self, app: &mut App) {
let physics_schedule = app
.get_schedule_mut(PhysicsSchedule)
.expect("add PhysicsSchedule first");
physics_schedule.add_systems(
(
update_ray_caster_positions,
#[cfg(all(
feature = "default-collider",
any(feature = "parry-f32", feature = "parry-f64")
))]
(update_shape_caster_positions, raycast, shapecast).chain(),
)
.chain()
.in_set(PhysicsStepSystems::SpatialQuery),
);
}
fn finish(&self, app: &mut App) {
app.register_physics_diagnostics::<SpatialQueryDiagnostics>();
}
}
type RayCasterPositionQueryComponents = (
&'static mut RayCaster,
Option<&'static Position>,
Option<&'static Rotation>,
Option<&'static ChildOf>,
Option<&'static GlobalTransform>,
);
#[allow(clippy::type_complexity)]
fn update_ray_caster_positions(
mut rays: Query<RayCasterPositionQueryComponents>,
parents: Query<
(
Option<&Position>,
Option<&Rotation>,
Option<&GlobalTransform>,
),
With<Children>,
>,
) {
for (mut ray, position, rotation, parent, transform) in &mut rays {
let origin = ray.origin;
let direction = ray.direction;
let global_position = position.copied().or(transform.map(Position::from));
let global_rotation = rotation.copied().or(transform.map(Rotation::from));
if let Some(global_position) = global_position {
ray.set_global_origin(global_position.0 + rotation.map_or(origin, |rot| rot * origin));
} else if parent.is_none() {
ray.set_global_origin(origin);
}
if let Some(global_rotation) = global_rotation {
let global_direction = global_rotation * ray.direction;
ray.set_global_direction(global_direction);
} else if parent.is_none() {
ray.set_global_direction(direction);
}
if let Some(Ok((parent_position, parent_rotation, parent_transform))) =
parent.map(|&ChildOf(parent)| parents.get(parent))
{
let parent_position = parent_position
.copied()
.or(parent_transform.map(Position::from));
let parent_rotation = parent_rotation
.copied()
.or(parent_transform.map(Rotation::from));
if global_position.is_none()
&& let Some(position) = parent_position
{
let rotation = global_rotation.unwrap_or(parent_rotation.unwrap_or_default());
ray.set_global_origin(position.0 + rotation * origin);
}
if global_rotation.is_none()
&& let Some(rotation) = parent_rotation
{
let global_direction = rotation * ray.direction;
ray.set_global_direction(global_direction);
}
}
}
}
#[cfg(any(feature = "parry-f32", feature = "parry-f64"))]
type ShapeCasterPositionQueryComponents = (
&'static mut ShapeCaster,
Option<&'static Position>,
Option<&'static Rotation>,
Option<&'static ChildOf>,
Option<&'static GlobalTransform>,
);
#[cfg(any(feature = "parry-f32", feature = "parry-f64"))]
#[allow(clippy::type_complexity)]
fn update_shape_caster_positions(
mut shape_casters: Query<ShapeCasterPositionQueryComponents>,
parents: Query<
(
Option<&Position>,
Option<&Rotation>,
Option<&GlobalTransform>,
),
With<Children>,
>,
) {
for (mut shape_caster, position, rotation, parent, transform) in &mut shape_casters {
let origin = shape_caster.origin;
let shape_rotation = shape_caster.shape_rotation;
let direction = shape_caster.direction;
let global_position = position.copied().or(transform.map(Position::from));
let global_rotation = rotation.copied().or(transform.map(Rotation::from));
if let Some(global_position) = global_position {
shape_caster
.set_global_origin(global_position.0 + rotation.map_or(origin, |rot| rot * origin));
} else if parent.is_none() {
shape_caster.set_global_origin(origin);
}
if let Some(global_rotation) = global_rotation {
let global_direction = global_rotation * shape_caster.direction;
shape_caster.set_global_direction(global_direction);
#[cfg(feature = "2d")]
{
shape_caster
.set_global_shape_rotation(shape_rotation + global_rotation.as_radians());
}
#[cfg(feature = "3d")]
{
shape_caster.set_global_shape_rotation(shape_rotation * global_rotation.0);
}
} else if parent.is_none() {
shape_caster.set_global_direction(direction);
#[cfg(feature = "2d")]
{
shape_caster.set_global_shape_rotation(shape_rotation);
}
#[cfg(feature = "3d")]
{
shape_caster.set_global_shape_rotation(shape_rotation);
}
}
if let Some(Ok((parent_position, parent_rotation, parent_transform))) =
parent.map(|&ChildOf(parent)| parents.get(parent))
{
let parent_position = parent_position
.copied()
.or(parent_transform.map(Position::from));
let parent_rotation = parent_rotation
.copied()
.or(parent_transform.map(Rotation::from));
if global_position.is_none()
&& let Some(position) = parent_position
{
let rotation = global_rotation.unwrap_or(parent_rotation.unwrap_or_default());
shape_caster.set_global_origin(position.0 + rotation * origin);
}
if global_rotation.is_none()
&& let Some(rotation) = parent_rotation
{
let global_direction = rotation * shape_caster.direction;
shape_caster.set_global_direction(global_direction);
#[cfg(feature = "2d")]
{
shape_caster.set_global_shape_rotation(shape_rotation + rotation.as_radians());
}
#[cfg(feature = "3d")]
{
shape_caster.set_global_shape_rotation(shape_rotation * rotation.0);
}
}
}
}
}
#[cfg(any(feature = "parry-f32", feature = "parry-f64"))]
fn raycast(
mut rays: Query<(Entity, &mut RayCaster, &mut RayHits)>,
spatial_query: SpatialQuery,
mut diagnostics: ResMut<SpatialQueryDiagnostics>,
) {
let start = crate::utils::Instant::now();
for (entity, mut ray_caster, mut hits) in &mut rays {
if ray_caster.enabled {
ray_caster.cast(entity, &mut hits, &spatial_query);
} else if !hits.is_empty() {
hits.clear();
}
}
diagnostics.update_ray_casters = start.elapsed();
}
#[cfg(any(feature = "parry-f32", feature = "parry-f64"))]
fn shapecast(
mut shape_casters: Query<(Entity, &mut ShapeCaster, &mut ShapeHits)>,
spatial_query: SpatialQuery,
mut diagnostics: ResMut<SpatialQueryDiagnostics>,
) {
let start = crate::utils::Instant::now();
for (entity, mut shape_caster, mut hits) in &mut shape_casters {
if shape_caster.enabled {
shape_caster.cast(entity, &mut hits, &spatial_query);
} else if !hits.is_empty() {
hits.clear();
}
}
diagnostics.update_shape_casters = start.elapsed();
}