diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index 38f277f8..da170406 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -82,35 +82,44 @@ impl RayIntersection { pub struct Toi { /// The time at which the objects touch. pub toi: Real, - /// The local-space closest point on the first shape at the time of impact. + /// Detail about the impact points. /// - /// Undefined if `status` is `Penetrating`. + /// `None` if `status` is `Penetrating`. + pub details: Option, + /// The way the time-of-impact computation algorithm terminated. + pub status: TOIStatus, +} + +/// In depth information about a time-of-impact (TOI) computation. +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct ToiDetails { + /// The local-space closest point on the first shape at the time of impact. pub witness1: Vect, /// The local-space closest point on the second shape at the time of impact. - /// - /// Undefined if `status` is `Penetrating`. pub witness2: Vect, /// The local-space outward normal on the first shape at the time of impact. - /// - /// Undefined if `status` is `Penetrating`. pub normal1: Vect, /// The local-space outward normal on the second shape at the time of impact. - /// - /// Undefined if `status` is `Penetrating`. pub normal2: Vect, - /// The way the time-of-impact computation algorithm terminated. - pub status: TOIStatus, } impl Toi { - pub(crate) fn from_rapier(physics_scale: Real, toi: rapier::parry::query::TOI) -> Self { + /// Convert from internal `rapier::Toi`. + pub fn from_rapier(physics_scale: Real, toi: rapier::parry::query::TOI) -> Self { + let details = if toi.status != TOIStatus::Penetrating { + Some(ToiDetails { + witness1: (toi.witness1 * physics_scale).into(), + witness2: (toi.witness2 * physics_scale).into(), + normal1: toi.normal1.into(), + normal2: toi.normal2.into(), + }) + } else { + None + }; Self { toi: toi.toi, - witness1: (toi.witness1 * physics_scale).into(), - witness2: (toi.witness2 * physics_scale).into(), - normal1: toi.normal1.into(), - normal2: toi.normal2.into(), status: toi.status, + details, } } } diff --git a/src/plugin/context.rs b/src/plugin/context.rs index 0dadbc47..041878d5 100644 --- a/src/plugin/context.rs +++ b/src/plugin/context.rs @@ -759,11 +759,19 @@ impl RapierContext { /// collider, and are in world space. /// /// # Parameters - /// * `shape_pos` - The initial position of the shape to cast. + /// * `shape_pos` - The initial translation of the shape to cast. + /// * `shape_rot` - The rotation of the shape to cast. /// * `shape_vel` - The constant velocity of the shape to cast (i.e. the cast direction). /// * `shape` - The shape to cast. /// * `max_toi` - The maximum time-of-impact that can be reported by this cast. This effectively /// limits the distance traveled by the shape to `shapeVel.norm() * maxToi`. + /// * `stop_at_penetration` - If the casted shape starts in a penetration state with any + /// collider, two results are possible. If `stop_at_penetration` is `true` then, the + /// result will have a `toi` equal to `start_time`. If `stop_at_penetration` is `false` + /// then the nonlinear shape-casting will see if further motion wrt. the penetration normal + /// would result in tunnelling. If it does not (i.e. we have a separating velocity along + /// that normal) then the nonlinear shape-casting will attempt to find another impact, + /// at a time `> start_time` that could result in tunnelling. /// * `filter`: set of rules used to determine which collider is taken into account by this scene query. #[allow(clippy::too_many_arguments)] pub fn cast_shape( @@ -773,6 +781,7 @@ impl RapierContext { shape_vel: Vect, shape: &Collider, max_toi: Real, + stop_at_penetration: bool, filter: QueryFilter, ) -> Option<(Entity, Toi)> { let scaled_transform = (shape_pos / self.physics_scale, shape_rot).into(); @@ -789,7 +798,7 @@ impl RapierContext { &(shape_vel / self.physics_scale).into(), &*scaled_shape.raw, max_toi, - true, + stop_at_penetration, filter, ) })?;