Fix EPA contact point for mesh triangles with long edges#2065
Fix EPA contact point for mesh triangles with long edges#2065AndreiPotapov wants to merge 1 commit into
Conversation
|
I've checked another one crash dump and removed fast-path check by bbox. It was passed by some invalid contacts. |
|
So far I've always managed to avoid extra checks like this by investigating what goes wrong inside the EPA algorithm. Could you please provide me with all the values of the Can you also let me know which version of Jolt / which platform / compiler / compiler flags you're using to compile Jolt? |
|
mShape1: CapsuleShape, mRadius = 0.25, mHalfHeightOfCylinder = 0.5 Problem mesh in local space of mShape1: Happened on linux, compiled by clang +-21, with -march=haswell or something near, but FMA was explicitly disabled for Jolt. So you can use -mavx. Nothing wrong with EPA algo. There an classic problem of mixing very small float32 with a big float32 in calculations. First one loosing most of it's preciosion. |
|
Added test, you should be able to reproduce it with CROSS_PLATFORM_DETERMINISTIC enabled. Fast math on, fma off. GJK also can produce bad contact instead of Status::Indeterminate, so I added workaround. |
c23bfd2 to
a1918f9
Compare
|
Added also point2 check after GJK |
|
Thanks, I will need some time to look at this properly. |
|
I got third crash. All of them was caused by a long needle-form triangle, formally Jolt doesn't consider them as degenerate. In all crash dumps GJK returned Indeterminate, but I can't get same result in my tests with the same data. So, there is definitely some dependency on the compiler and its options, but the main problem lies in the limitations of float32. Our compilation options are quite conservative for games, things that usually breaks precision was disabled: Triangles data from 3 crashes:
Our level designers will try to avoid such strange triangles in collisions, but I think the technical problem of how to validate or fix it remains open. It was hard to investigate the root reason why our phys objects sometimes acquire cosmic speeds. |
|
Added one more validation, if both points are not fixable after EPA, ignore contact in that case. |
When a convex shape collides with a mesh triangle whose edge length is much larger than the penetration depth (e.g. ~84 m edge vs ~5 cm penetration), EPA converges to the wrong Minkowski polytope facet due to float32 precision loss. GetPenetrationDepthStepEPA returns true but point2 can end up far from point1, producing degenerate contact data. If point2 is farther from point1 than the query shape bounding-box diagonal, snap it to the closest point on the triangle and update penetration_axis. On the same geometry GJK can also return EStatus::Colliding with a garbage point1; that is caught by checking point1 against a 2x-expanded mBoundsOf1 and redirecting to EPA. Reproducible with open-world game levels where large floor/wall tiles are used directly as physics mesh geometry. Subdividing such meshes is not always viable: the BVH codec has a hard size limit that is exceeded when large terrain meshes are split to a safe edge length. Regression test uses the exact triangle vertices from a production crash. Requires CROSS_PLATFORM_DETERMINISTIC to reproduce; with FMA enabled the GJK arithmetic avoids the bad path.
When a convex shape collides with a mesh triangle whose edge length is much larger than the penetration depth (e.g. ~84m edge vs ~5cm penetration, a ~1700:1 ratio), EPA converges to the wrong Minkowski polytope facet due to float32 precision loss. GetPenetrationDepthStepEPA returns true but point2 can be tens of meters outside the triangle, producing degenerate contact data.
The fix runs after GetPenetrationDepthStepEPA in CollideConvexVsTriangles::Collide. A valid point2 lies on the triangle and must be inside its axis-aligned bounding box — this is the fast-path check (six float compares, no cost in the normal case). If point2 is outside the bbox, we compute the actual closest point on the triangle to point1 and, if point2 is farther from it than the query shape's AABB diagonal, snap point2 there and update penetration_axis. The two-level structure ensures correctness: any epsilon-outside false positives from the bbox check are caught by the distance threshold and produce no correction.
This is reproducible with open-world game levels where large floor/wall tiles are used directly as physics mesh geometry. Subdividing such meshes is not always viable - the BVH codec has a hard size limit that is exceeded when large terrain meshes are split to a safe edge length.
Actual data that caused assert on bad contact point produced:
Level geometry mesh vertices: (-75.6, 15.0, 320.2), (-112.7, 15.0, 245.4), (-104.2, 15.0, 262.5) - longest edge 83.5m, flat floor tile
Vehicle position: approximately (-90, 15.05, 300) - 5cm above the triangle plane
Actual penetration depth: ~5cm, scale ratio ~1700:1
EPA reported point2 = (-70.21, 15.0, 330.99) - 84m outside the triangle
Resulting contact: point1 = (-88.4, 15.05, 298.7), point2 = (-54.6, 15.0, 332.5), distance 34m, penetration depth -33.85m