use crate::dynamics::RigidBodyHandle;
use crate::geometry::{Aabb, Collider, ColliderHandle, PointProjection, Ray, RayIntersection};
use crate::geometry::{BroadPhaseBvh, InteractionGroups};
use crate::math::{Pose, Real, Vector};
use crate::{dynamics::RigidBodySet, geometry::ColliderSet};
use parry::bounding_volume::BoundingVolume;
use parry::partitioning::{Bvh, BvhNode};
use parry::query::details::{NormalConstraints, ShapeCastOptions};
use parry::query::{NonlinearRigidMotion, QueryDispatcher, RayCast, ShapeCastHit};
use parry::shape::{CompositeShape, CompositeShapeRef, FeatureId, Shape, TypedCompositeShape};
#[derive(Copy, Clone)]
pub struct QueryPipeline<'a> {
pub dispatcher: &'a dyn QueryDispatcher,
pub bvh: &'a Bvh,
pub bodies: &'a RigidBodySet,
pub colliders: &'a ColliderSet,
pub filter: QueryFilter<'a>,
}
pub struct QueryPipelineMut<'a> {
pub dispatcher: &'a dyn QueryDispatcher,
pub bvh: &'a Bvh,
pub bodies: &'a mut RigidBodySet,
pub colliders: &'a mut ColliderSet,
pub filter: QueryFilter<'a>,
}
impl QueryPipelineMut<'_> {
pub fn as_ref(&self) -> QueryPipeline<'_> {
QueryPipeline {
dispatcher: self.dispatcher,
bvh: self.bvh,
bodies: &*self.bodies,
colliders: &*self.colliders,
filter: self.filter,
}
}
}
impl CompositeShape for QueryPipeline<'_> {
fn map_part_at(
&self,
shape_id: u32,
f: &mut dyn FnMut(Option<&Pose>, &dyn Shape, Option<&dyn NormalConstraints>),
) {
self.map_untyped_part_at(shape_id, f);
}
fn bvh(&self) -> &Bvh {
self.bvh
}
}
impl TypedCompositeShape for QueryPipeline<'_> {
type PartNormalConstraints = ();
type PartShape = dyn Shape;
fn map_typed_part_at<T>(
&self,
shape_id: u32,
mut f: impl FnMut(Option<&Pose>, &Self::PartShape, Option<&Self::PartNormalConstraints>) -> T,
) -> Option<T> {
let (co, co_handle) = self.colliders.get_unknown_gen(shape_id)?;
if self.filter.test(self.bodies, co_handle, co) {
Some(f(Some(co.position()), co.shape(), None))
} else {
None
}
}
fn map_untyped_part_at<T>(
&self,
shape_id: u32,
mut f: impl FnMut(Option<&Pose>, &dyn Shape, Option<&dyn NormalConstraints>) -> T,
) -> Option<T> {
let (co, co_handle) = self.colliders.get_unknown_gen(shape_id)?;
if self.filter.test(self.bodies, co_handle, co) {
Some(f(Some(co.position()), co.shape(), None))
} else {
None
}
}
}
impl BroadPhaseBvh {
pub fn as_query_pipeline<'a>(
&'a self,
dispatcher: &'a dyn QueryDispatcher,
bodies: &'a RigidBodySet,
colliders: &'a ColliderSet,
filter: QueryFilter<'a>,
) -> QueryPipeline<'a> {
QueryPipeline {
dispatcher,
bvh: &self.tree,
bodies,
colliders,
filter,
}
}
pub fn as_query_pipeline_mut<'a>(
&'a self,
dispatcher: &'a dyn QueryDispatcher,
bodies: &'a mut RigidBodySet,
colliders: &'a mut ColliderSet,
filter: QueryFilter<'a>,
) -> QueryPipelineMut<'a> {
QueryPipelineMut {
dispatcher,
bvh: &self.tree,
bodies,
colliders,
filter,
}
}
}
impl<'a> QueryPipeline<'a> {
fn id_to_handle<T>(&self, (id, data): (u32, T)) -> Option<(ColliderHandle, T)> {
self.colliders.get_unknown_gen(id).map(|(_, h)| (h, data))
}
pub fn with_filter(self, filter: QueryFilter<'a>) -> Self {
Self { filter, ..self }
}
#[profiling::function]
pub fn cast_ray(
&self,
ray: &Ray,
max_toi: Real,
solid: bool,
) -> Option<(ColliderHandle, Real)> {
CompositeShapeRef(self)
.cast_local_ray(ray, max_toi, solid)
.and_then(|hit| self.id_to_handle(hit))
}
#[profiling::function]
pub fn cast_ray_and_get_normal(
&self,
ray: &Ray,
max_toi: Real,
solid: bool,
) -> Option<(ColliderHandle, RayIntersection)> {
CompositeShapeRef(self)
.cast_local_ray_and_get_normal(ray, max_toi, solid)
.and_then(|hit| self.id_to_handle(hit))
}
#[profiling::function]
pub fn intersect_ray(
&'a self,
ray: Ray,
max_toi: Real,
solid: bool,
) -> impl Iterator<Item = (ColliderHandle, &'a Collider, RayIntersection)> + 'a {
self.bvh
.leaves(move |node: &BvhNode| node.aabb().intersects_local_ray(&ray, max_toi))
.filter_map(move |leaf| {
let (co, co_handle) = self.colliders.get_unknown_gen(leaf)?;
if self.filter.test(self.bodies, co_handle, co) {
if let Some(intersection) =
co.shape
.cast_ray_and_get_normal(co.position(), &ray, max_toi, solid)
{
return Some((co_handle, co, intersection));
}
}
None
})
}
#[profiling::function]
pub fn project_point(
&self,
point: Vector,
_max_dist: Real,
solid: bool,
) -> Option<(ColliderHandle, PointProjection)> {
self.id_to_handle(CompositeShapeRef(self).project_local_point(point, solid))
}
#[profiling::function]
pub fn intersect_point(
&'a self,
point: Vector,
) -> impl Iterator<Item = (ColliderHandle, &'a Collider)> + 'a {
self.bvh
.leaves(move |node: &BvhNode| node.aabb().contains_local_point(point))
.filter_map(move |leaf| {
let (co, co_handle) = self.colliders.get_unknown_gen(leaf)?;
if self.filter.test(self.bodies, co_handle, co)
&& co.shape.contains_point(co.position(), point)
{
return Some((co_handle, co));
}
None
})
}
#[profiling::function]
pub fn project_point_and_get_feature(
&self,
point: Vector,
) -> Option<(ColliderHandle, PointProjection, FeatureId)> {
let (id, (proj, feat)) = CompositeShapeRef(self).project_local_point_and_get_feature(point);
let handle = self.colliders.get_unknown_gen(id)?.1;
Some((handle, proj, feat))
}
#[profiling::function]
pub fn intersect_aabb_conservative(
&'a self,
aabb: Aabb,
) -> impl Iterator<Item = (ColliderHandle, &'a Collider)> + 'a {
self.bvh
.leaves(move |node: &BvhNode| node.aabb().intersects(&aabb))
.filter_map(move |leaf| {
let (co, co_handle) = self.colliders.get_unknown_gen(leaf)?;
if self.filter.test(self.bodies, co_handle, co) {
return Some((co_handle, co));
}
None
})
}
#[profiling::function]
pub fn cast_shape(
&self,
shape_pos: &Pose,
shape_vel: Vector,
shape: &dyn Shape,
options: ShapeCastOptions,
) -> Option<(ColliderHandle, ShapeCastHit)> {
CompositeShapeRef(self)
.cast_shape(self.dispatcher, shape_pos, shape_vel, shape, options)
.and_then(|hit| self.id_to_handle(hit))
}
#[profiling::function]
pub fn cast_shape_nonlinear(
&self,
shape_motion: &NonlinearRigidMotion,
shape: &dyn Shape,
start_time: Real,
end_time: Real,
stop_at_penetration: bool,
) -> Option<(ColliderHandle, ShapeCastHit)> {
CompositeShapeRef(self)
.cast_shape_nonlinear(
self.dispatcher,
&NonlinearRigidMotion::identity(),
shape_motion,
shape,
start_time,
end_time,
stop_at_penetration,
)
.and_then(|hit| self.id_to_handle(hit))
}
#[profiling::function]
pub fn intersect_shape(
&'a self,
shape_pos: Pose,
shape: &'a dyn Shape,
) -> impl Iterator<Item = (ColliderHandle, &'a Collider)> + 'a {
let shape_aabb = shape.compute_aabb(&shape_pos);
self.bvh
.leaves(move |node: &BvhNode| node.aabb().intersects(&shape_aabb))
.filter_map(move |leaf| {
let (co, co_handle) = self.colliders.get_unknown_gen(leaf)?;
if self.filter.test(self.bodies, co_handle, co) {
let pos12 = shape_pos.inv_mul(co.position());
if self.dispatcher.intersection_test(&pos12, shape, co.shape()) == Ok(true) {
return Some((co_handle, co));
}
}
None
})
}
}
bitflags::bitflags! {
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub struct QueryFilterFlags: u32 {
const EXCLUDE_FIXED = 1 << 0;
const EXCLUDE_KINEMATIC = 1 << 1;
const EXCLUDE_DYNAMIC = 1 << 2;
const EXCLUDE_SENSORS = 1 << 3;
const EXCLUDE_SOLIDS = 1 << 4;
const ONLY_DYNAMIC = Self::EXCLUDE_FIXED.bits() | Self::EXCLUDE_KINEMATIC.bits();
const ONLY_KINEMATIC = Self::EXCLUDE_DYNAMIC.bits() | Self::EXCLUDE_FIXED.bits();
const ONLY_FIXED = Self::EXCLUDE_DYNAMIC.bits() | Self::EXCLUDE_KINEMATIC.bits();
}
}
impl QueryFilterFlags {
#[inline]
pub fn test(&self, bodies: &RigidBodySet, collider: &Collider) -> bool {
if self.is_empty() {
return true;
}
if (self.contains(QueryFilterFlags::EXCLUDE_SENSORS) && collider.is_sensor())
|| (self.contains(QueryFilterFlags::EXCLUDE_SOLIDS) && !collider.is_sensor())
{
return false;
}
if self.contains(QueryFilterFlags::EXCLUDE_FIXED) && collider.parent.is_none() {
return false;
}
if let Some(parent) = collider.parent.and_then(|p| bodies.get(p.handle)) {
let parent_type = parent.body_type();
if (self.contains(QueryFilterFlags::EXCLUDE_FIXED) && parent_type.is_fixed())
|| (self.contains(QueryFilterFlags::EXCLUDE_KINEMATIC)
&& parent_type.is_kinematic())
|| (self.contains(QueryFilterFlags::EXCLUDE_DYNAMIC) && parent_type.is_dynamic())
{
return false;
}
}
true
}
}
#[derive(Copy, Clone, Default)]
pub struct QueryFilter<'a> {
pub flags: QueryFilterFlags,
pub groups: Option<InteractionGroups>,
pub exclude_collider: Option<ColliderHandle>,
pub exclude_rigid_body: Option<RigidBodyHandle>,
#[allow(clippy::type_complexity)]
pub predicate: Option<&'a dyn Fn(ColliderHandle, &Collider) -> bool>,
}
impl QueryFilter<'_> {
#[inline]
pub fn test(&self, bodies: &RigidBodySet, handle: ColliderHandle, collider: &Collider) -> bool {
self.exclude_collider != Some(handle)
&& (self.exclude_rigid_body.is_none() || self.exclude_rigid_body != collider.parent.map(|p| p.handle))
&& self
.groups
.map(|grps| collider.flags.collision_groups.test(grps))
.unwrap_or(true)
&& self.flags.test(bodies, collider)
&& self.predicate.map(|f| f(handle, collider)).unwrap_or(true)
}
}
impl From<QueryFilterFlags> for QueryFilter<'_> {
fn from(flags: QueryFilterFlags) -> Self {
Self {
flags,
..QueryFilter::default()
}
}
}
impl From<InteractionGroups> for QueryFilter<'_> {
fn from(groups: InteractionGroups) -> Self {
Self {
groups: Some(groups),
..QueryFilter::default()
}
}
}
impl<'a> QueryFilter<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn exclude_fixed() -> Self {
QueryFilterFlags::EXCLUDE_FIXED.into()
}
pub fn exclude_kinematic() -> Self {
QueryFilterFlags::EXCLUDE_KINEMATIC.into()
}
pub fn exclude_dynamic() -> Self {
QueryFilterFlags::EXCLUDE_DYNAMIC.into()
}
pub fn only_dynamic() -> Self {
QueryFilterFlags::ONLY_DYNAMIC.into()
}
pub fn only_kinematic() -> Self {
QueryFilterFlags::ONLY_KINEMATIC.into()
}
pub fn only_fixed() -> Self {
QueryFilterFlags::ONLY_FIXED.into()
}
pub fn exclude_sensors(mut self) -> Self {
self.flags |= QueryFilterFlags::EXCLUDE_SENSORS;
self
}
pub fn exclude_solids(mut self) -> Self {
self.flags |= QueryFilterFlags::EXCLUDE_SOLIDS;
self
}
pub fn groups(mut self, groups: InteractionGroups) -> Self {
self.groups = Some(groups);
self
}
pub fn exclude_collider(mut self, collider: ColliderHandle) -> Self {
self.exclude_collider = Some(collider);
self
}
pub fn exclude_rigid_body(mut self, rigid_body: RigidBodyHandle) -> Self {
self.exclude_rigid_body = Some(rigid_body);
self
}
pub fn predicate(mut self, predicate: &'a impl Fn(ColliderHandle, &Collider) -> bool) -> Self {
self.predicate = Some(predicate);
self
}
}