use crate::{collision::contact_types::SingleContact, prelude::*};
use bevy::prelude::*;
use parry::query::{PersistentQueryDispatcher, ShapeCastOptions, Unsupported};
pub type UnsupportedShape = Unsupported;
pub fn contact(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
prediction_distance: Scalar,
) -> Result<Option<SingleContact>, UnsupportedShape> {
let rotation1: Rotation = rotation1.into();
let rotation2: Rotation = rotation2.into();
let isometry1 = make_pose(position1, rotation1);
let isometry2 = make_pose(position2, rotation2);
parry::query::contact(
&isometry1,
collider1.shape_scaled().0.as_ref(),
&isometry2,
collider2.shape_scaled().0.as_ref(),
prediction_distance,
)
.map(|contact| {
if let Some(contact) = contact {
let point1: Vector = rotation1.inverse() * contact.point1;
let point2: Vector = rotation2.inverse() * contact.point2;
let normal1: Vector = (rotation1.inverse() * contact.normal1).normalize();
let normal2: Vector = (rotation2.inverse() * contact.normal2).normalize();
if !normal1.is_normalized() || !normal2.is_normalized() {
return None;
}
Some(SingleContact::new(
point1,
point2,
normal1,
normal2,
-contact.dist,
))
} else {
None
}
})
}
pub fn contact_manifolds(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
prediction_distance: Scalar,
manifolds: &mut Vec<ContactManifold>,
) {
let position1: Position = position1.into();
let position2: Position = position2.into();
let rotation1: Rotation = rotation1.into();
let rotation2: Rotation = rotation2.into();
let isometry1 = make_pose(position1, rotation1);
let isometry2 = make_pose(position2, rotation2);
let isometry12 = isometry1.inv_mul(&isometry2);
let mut new_manifolds =
Vec::<parry::query::ContactManifold<(), ()>>::with_capacity(manifolds.len());
let result = parry::query::DefaultQueryDispatcher.contact_manifolds(
&isometry12,
collider1.shape_scaled().0.as_ref(),
collider2.shape_scaled().0.as_ref(),
prediction_distance,
&mut new_manifolds,
&mut None,
);
manifolds.clear();
if result.is_err()
&& let (Some(shape1), Some(shape2)) = (
collider1.shape_scaled().as_support_map(),
collider2.shape_scaled().as_support_map(),
)
&& let Some(contact) = parry::query::contact::contact_support_map_support_map(
&isometry12,
shape1,
shape2,
prediction_distance,
)
{
let normal = rotation1 * contact.normal1;
if !normal.is_normalized() {
return;
}
let local_point1: Vector = contact.point1;
let point1 = rotation1 * local_point1;
let anchor1 = point1 + normal * contact.dist * 0.5;
let anchor2 = anchor1 + (position1.0 - position2.0);
let world_point = position1.0 + anchor1;
let points = [ContactPoint::new(
anchor1,
anchor2,
world_point,
-contact.dist,
)];
manifolds.push(ContactManifold::new(points, normal));
}
manifolds.extend(new_manifolds.iter().filter_map(|manifold| {
if manifold.contacts().is_empty() {
return None;
}
let subpos1 = manifold.subshape_pos1.unwrap_or_default();
let local_normal: Vector = (subpos1.rotation * manifold.local_n1).normalize();
let normal = rotation1 * local_normal;
if !normal.is_normalized() {
return None;
}
let points = manifold.contacts().iter().map(|contact| {
let point1 = rotation1 * subpos1.transform_point(contact.local_p1);
let anchor1 = point1 + normal * contact.dist * 0.5;
let anchor2 = anchor1 + (position1.0 - position2.0);
let world_point = position1.0 + anchor1;
ContactPoint::new(anchor1, anchor2, world_point, -contact.dist)
.with_feature_ids(contact.fid1.into(), contact.fid2.into())
});
let manifold = ContactManifold::new(points, normal);
Some(manifold)
}));
}
#[derive(Reflect, Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, PartialEq)]
pub enum ClosestPoints {
Intersecting,
WithinMargin(Vector, Vector),
OutsideMargin,
}
pub fn closest_points(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
max_distance: Scalar,
) -> Result<ClosestPoints, UnsupportedShape> {
let rotation1: Rotation = rotation1.into();
let rotation2: Rotation = rotation2.into();
let isometry1 = make_pose(position1, rotation1);
let isometry2 = make_pose(position2, rotation2);
parry::query::closest_points(
&isometry1,
collider1.shape_scaled().0.as_ref(),
&isometry2,
collider2.shape_scaled().0.as_ref(),
max_distance,
)
.map(|closest_points| match closest_points {
parry::query::ClosestPoints::Intersecting => ClosestPoints::Intersecting,
parry::query::ClosestPoints::WithinMargin(point1, point2) => {
ClosestPoints::WithinMargin(point1, point2)
}
parry::query::ClosestPoints::Disjoint => ClosestPoints::OutsideMargin,
})
}
pub fn distance(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
) -> Result<Scalar, UnsupportedShape> {
let rotation1: Rotation = rotation1.into();
let rotation2: Rotation = rotation2.into();
let isometry1 = make_pose(position1, rotation1);
let isometry2 = make_pose(position2, rotation2);
parry::query::distance(
&isometry1,
collider1.shape_scaled().0.as_ref(),
&isometry2,
collider2.shape_scaled().0.as_ref(),
)
}
pub fn intersection_test(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
) -> Result<bool, UnsupportedShape> {
let rotation1: Rotation = rotation1.into();
let rotation2: Rotation = rotation2.into();
let isometry1 = make_pose(position1, rotation1);
let isometry2 = make_pose(position2, rotation2);
parry::query::intersection_test(
&isometry1,
collider1.shape_scaled().0.as_ref(),
&isometry2,
collider2.shape_scaled().0.as_ref(),
)
}
pub type TimeOfImpactStatus = parry::query::details::ShapeCastStatus;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TimeOfImpact {
pub time_of_impact: Scalar,
pub point1: Vector,
pub point2: Vector,
pub normal1: Vector,
pub normal2: Vector,
pub status: TimeOfImpactStatus,
}
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
pub fn time_of_impact(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
velocity1: impl Into<LinearVelocity>,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
velocity2: impl Into<LinearVelocity>,
max_time_of_impact: Scalar,
) -> Result<Option<TimeOfImpact>, UnsupportedShape> {
let rotation1: Rotation = rotation1.into();
let rotation2: Rotation = rotation2.into();
let velocity1: LinearVelocity = velocity1.into();
let velocity2: LinearVelocity = velocity2.into();
let isometry1 = make_pose(position1, rotation1);
let isometry2 = make_pose(position2, rotation2);
parry::query::cast_shapes(
&isometry1,
velocity1.0,
collider1.shape_scaled().0.as_ref(),
&isometry2,
velocity2.0,
collider2.shape_scaled().0.as_ref(),
ShapeCastOptions {
max_time_of_impact,
stop_at_penetration: true,
..default()
},
)
.map(|toi| {
toi.map(|toi| TimeOfImpact {
time_of_impact: toi.time_of_impact,
point1: toi.witness1,
point2: toi.witness2,
normal1: toi.normal1,
normal2: toi.normal2,
status: toi.status,
})
})
}