diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index e182e981..7207df68 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -97,6 +97,12 @@ impl<'a> From<&'a Collider> for &'a dyn Shape { } } +impl AsRef for Collider { + fn as_ref(&self) -> &dyn Shape { + &*self.raw + } +} + impl fmt::Debug for Collider { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.as_typed_shape().fmt(f) diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index da170406..1b6ad356 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -10,6 +10,8 @@ use rapier::prelude::FeatureId; mod collider; mod collider_impl; +/// Wrappers around Parry shape queries. +pub mod query; /// Wrappers around Rapier shapes to access their properties. pub mod shape_views; diff --git a/src/geometry/query/mod.rs b/src/geometry/query/mod.rs new file mode 100644 index 00000000..b49e99ca --- /dev/null +++ b/src/geometry/query/mod.rs @@ -0,0 +1,104 @@ +use crate::{ + parry::{ + self, + query::{Contact, Unsupported}, + shape::Shape, + }, + prelude::*, + utils::pos_rot_to_iso, +}; + +/// Results from [`closest_points`] +pub enum ClosestPoints { + /// The two objects are intersecting. + Intersecting, + /// The two objects are non-intersecting but closer than a given user-defined distance. + WithinMargin(Vect, Vect), + /// The two objects are non-intersecting and further than a given user-defined distance. + Disjoint, +} + +use parry::query::closest_points::ClosestPoints as ParryClosestPoints; +impl From for ClosestPoints { + fn from(parry: ParryClosestPoints) -> ClosestPoints { + match parry { + ParryClosestPoints::Intersecting => ClosestPoints::Intersecting, + ParryClosestPoints::Disjoint => ClosestPoints::Disjoint, + ParryClosestPoints::WithinMargin(p1, p2) => { + ClosestPoints::WithinMargin(p1.into(), p2.into()) + } + } + } +} + +/// Computes the pair of closest points between two shapes. +/// +/// Returns `ClosestPoints::Disjoint` if the objects are separated by a distance greater than `max_dist`. The result points in `ClosestPoints::WithinMargin` are expressed in world-space. +pub fn closest_points( + pos1: Vect, + rot1: Rot, + shape1: &dyn Shape, + pos2: Vect, + rot2: Rot, + shape2: &dyn Shape, + max_dist: Real, + physics_scale: Real, +) -> Result { + let iso1 = pos_rot_to_iso(pos1, rot1, physics_scale); + let iso2 = pos_rot_to_iso(pos2, rot2, physics_scale); + + parry::query::closest_points(&iso1, shape1, &iso2, shape2, max_dist).map(|parry| parry.into()) +} + +/// Computes the minimum distance separating two shapes. +/// +/// Returns 0.0 if the objects are touching or penetrating. +pub fn distance( + pos1: Vect, + rot1: Rot, + shape1: &dyn Shape, + pos2: Vect, + rot2: Rot, + shape2: &dyn Shape, + physics_scale: Real, +) -> Result { + let iso1 = pos_rot_to_iso(pos1, rot1, physics_scale); + let iso2 = pos_rot_to_iso(pos2, rot2, physics_scale); + + parry::query::distance(&iso1, shape1, &iso2, shape2) +} + +/// Computes one pair of contact points point between two shapes. +/// +/// Returns None if the objects are separated by a distance greater than prediction. The result is given in world-space. +pub fn contact( + pos1: Vect, + rot1: Rot, + shape1: &dyn Shape, + pos2: Vect, + rot2: Rot, + shape2: &dyn Shape, + prediction: Real, + physics_scale: Real, +) -> Result, Unsupported> { + let iso1 = pos_rot_to_iso(pos1, rot1, physics_scale); + let iso2 = pos_rot_to_iso(pos2, rot2, physics_scale); + + parry::query::contact(&iso1, shape1, &iso2, shape2, prediction) +} + +/// Tests whether two shapes are intersecting. +pub fn intersection_test( + pos1: Vect, + rot1: Rot, + shape1: &dyn Shape, + pos2: Vect, + rot2: Rot, + shape2: &dyn Shape, + physics_scale: Real, +) -> Result { + let iso1 = pos_rot_to_iso(pos1, rot1, physics_scale); + let iso2 = pos_rot_to_iso(pos2, rot2, physics_scale); + + parry::query::intersection_test(&iso1, shape1, &iso2, shape2) +} diff --git a/src/lib.rs b/src/lib.rs index 557cd268..2c25c4b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,6 +66,8 @@ pub mod render; /// Miscellaneous helper functions. pub mod utils; +pub use crate::geometry::query; + /// Groups the most often used types. pub mod prelude { pub use crate::control::*; diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 5523dac0..e118dc12 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,4 @@ +use crate::prelude::*; use bevy::prelude::Transform; use rapier::math::{Isometry, Real}; @@ -31,21 +32,35 @@ pub fn iso_to_transform(iso: &Isometry, physics_scale: Real) -> Transform #[cfg(feature = "dim2")] pub(crate) fn transform_to_iso(transform: &Transform, physics_scale: Real) -> Isometry { use bevy::math::Vec3Swizzles; - Isometry::new( - (transform.translation / physics_scale).xy().into(), + pos_rot_to_iso( + transform.translation.xy(), transform.rotation.to_scaled_axis().z, + physics_scale, ) } +/// Converts a translation and rotation into a Rapier isometry. +/// +/// The translation is divided by the `physics_scale`. +#[cfg(feature = "dim2")] +pub(crate) fn pos_rot_to_iso(pos: Vect, rot: Rot, physics_scale: Real) -> Isometry { + Isometry::new((pos / physics_scale).into(), rot) +} + /// Converts a Bevy transform to a Rapier isometry. /// /// The translation is divided by the `physics_scale`. #[cfg(feature = "dim3")] pub(crate) fn transform_to_iso(transform: &Transform, physics_scale: Real) -> Isometry { - Isometry::from_parts( - (transform.translation / physics_scale).into(), - transform.rotation.into(), - ) + pos_rot_to_iso(transform.translation, transform.rotation, physics_scale) +} + +/// Converts a translation and rotation into a Rapier isometry. +/// +/// The translation is divided by the `physics_scale`. +#[cfg(feature = "dim3")] +pub(crate) fn pos_rot_to_iso(pos: Vect, rot: Rot, physics_scale: Real) -> Isometry { + Isometry::from_parts((pos / physics_scale).into(), rot.into()) } #[cfg(test)]