Repository navigation
GJK never early-stops below the contact threshold (no placeholder contacts) - #915
Conversation
|
Thanks @quentinll Here the |
Build the convex from the points and triangles computed by ConvexBase32::convexHull, so that the test runs without Qhull, and use std::make_shared.
|
Approved on my side. @lmontaut If you want this PR to address other issues you mentioned (And I think it make sense to address them in the same PR) request some changes to avoid someone merge it. |
|
@jorisv I think we can merge this PR. If we find other stuff like that on our side, we will let you know. Thanks! |
|
Ok, it will be merged after #885 |
A cylinder against a convex, with distance_upper_bound = security_margin = 0 and a cached GJK guess. GJK early-stops at a distance of 3.4e-13, below collision_distance_threshold (1e-12), and collide() registers a contact whose normal is DBL_MAX and point is infinite.
The collision functions register a contact when distance - security_margin <= collision_distance_threshold, but GJK early-stopped as soon as the distance was above max(distance_upper_bound, security_margin). In between, an early stop, whose witness points and normal are placeholders, became a contact. GJKSolver::set now gives GJK an upper bound of at least security_margin + collision_distance_threshold, which fixes every collider sharing the solver. The early-stop branch also raises the returned distance to just above the upper bound, so that rounding errors cannot bring it back below the collider's threshold.
Build the convex from the points and triangles computed by ConvexBase32::convexHull, so that the test runs without Qhull, and use std::make_shared.
b367f38 to
eb8532a
Compare
Description
collidecan return a contact made of the placeholder values GJK writes when it stops early: a normal atDBL_MAX, a point at infinity and a tiny positive depth. Anything built from such a contact, such as a contact patch, is then non-finite.Cause
Two places decide whether the shapes are separated, with different thresholds:
distance_upper_bound = security_margin = 0GJK::evaluate)max(0, distance_upper_bound, security_margin)d - security_margin <= collision_distance_thresholdGJKSolver::setbuilds GJK's upper bound withoutcollision_distance_threshold. In the windowFix
GJKSolver::setgives GJK an upper bound of at leastsecurity_margin + collision_distance_threshold. Every pair that can be registered as a contact then runs GJK to convergence and gets real witness points and a real normal. All collision functions (shapes, BVHs, height fields, octrees) share the solver, so this covers all of them.GJKSolver::runGJKAndEPAraises the returned distance to just abovedistance_upper_bound(std::nextafter), so that rounding errors cannot bring an early stop back below the contact threshold.CollisionRequest::distance_upper_boundis documented accordingly.Checking GJK's status in the collision functions instead was rejected: some specialized pairs only run GJK sometimes, so the status can be stale, and it would need a check at every call site.
Test
early_stop_within_collision_distance_thresholdintest/security_margin.cppreproduces an early stop inside the window, and checks that every contact returned has a finite point and depth and a unit normal. It fails without the fix and passes with it; the full test suite passes.Checklist
pre-commit run --all-filesorpixi run lint