From f06cd56f1317f4d1be33a6eae70ecc88a422d0e3 Mon Sep 17 00:00:00 2001 From: dajiaohuang Date: Fri, 16 Oct 2026 19:33:52 +0800 Subject: [PATCH] fix(gjk): scale shape-cast convergence tolerance --- crates/parry3d-f64/tests/shape_cast.rs | 28 ++++++++++++++++++++++++++ src/query/gjk/gjk.rs | 6 +++++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 crates/parry3d-f64/tests/shape_cast.rs diff --git a/crates/parry3d-f64/tests/shape_cast.rs b/crates/parry3d-f64/tests/shape_cast.rs new file mode 100644 index 00000000..b4578180 --- /dev/null +++ b/crates/parry3d-f64/tests/shape_cast.rs @@ -0,0 +1,28 @@ +use parry3d_f64::math::{Pose, Vector}; +use parry3d_f64::query::{cast_shapes, ShapeCastOptions}; +use parry3d_f64::shape::Cuboid; + +#[test] +fn cast_starting_at_touching_boundary_and_moving_inward_hits() { + let floor = Cuboid::new(Vector::new(38.0, 0.5, 4.0)); + let floor_pos = Pose::from_translation(Vector::new(42.0, -0.5, 2.0)); + let boxy = Cuboid::new(Vector::splat(0.25)); + let options = ShapeCastOptions { + max_time_of_impact: 6.54806820868758261e-3, + target_distance: 0.01, + stop_at_penetration: false, + compute_impact_geometry_on_penetration: true, + }; + let start = Vector::new( + f64::from_bits(0x4038019999998b41), + f64::from_bits(0x3fd0a3d70a3d7099), + f64::from_bits(0xbe095faa25f26d8d), + ); + let dir = Vector::new(9.54479978035029708e-1, -2.98274993135946798e-1, 0.0); + + for dy in [0.0, -1e-9, 1e-9] { + let pos = Pose::from_translation(start + Vector::Y * dy); + let hit = cast_shapes(&floor_pos, Vector::ZERO, &floor, &pos, dir, &boxy, options).unwrap(); + assert!(hit.is_some(), "cast missed with start offset {dy}"); + } +} diff --git a/src/query/gjk/gjk.rs b/src/query/gjk/gjk.rs index 56f4caea..1f68e851 100644 --- a/src/query/gjk/gjk.rs +++ b/src/query/gjk/gjk.rs @@ -741,7 +741,11 @@ where let neg_proj_coords = -proj; let (normalized, dist) = neg_proj_coords.normalize_and_length(); - if dist > _eps_tol { + let mut support_scale: Real = 1.0; + for i in 0..=simplex.dimension() { + support_scale = support_scale.max((simplex.point(i).point + curr_ray.origin).length()); + } + if dist > _eps_tol * support_scale { dir = normalized; max_bound = dist; } else {