jrouwe / JoltPhysics

A multi core friendly rigid body physics and collision detection library. Written in C++. Suitable for games and VR applications. Used by Horizon Forbidden West.
MIT License
6.77k stars 453 forks source link

Surprising `CollideShape` results #419

Closed MDZhB closed 1 year ago

MDZhB commented 1 year ago

I've been dealing with odd results from CollideShape. I have two shapes decorated with RotatedTranslatedShape: a box and a capsule. The shapes are around 0.01 units apart, but CollideShape reports a penetration depth of around 0.5. mMaxSeparationDistance is at roughly 0.02 for this query.

This only happens occasionally and at specific capsule positions. I can move the capsule elsewhere at roughly the same distance to the box's surface and get results that look reasonable to me. Am I doing something wrong, or is this a bug?

I've diagrammed an example situation and put together a minimal program that reproduces the issue.

The shapes are arranged like this. The box shape's centre of mass is at roughly (6.92188, 0, 6.5). However, the box body's position is at (0, 0, 0) -- the shape is offset using RotatedTranslatedShape.

image

Although the contact points tend to lie near the shapes' surfaces, they are very far apart in a way that looks incorrect.

image

Penetration depth is 0.499005, although the shapes are slightly closer than maximum separation distance. The penetration axis is (0.037024, 0.034514, -0.000000), which looks like this:

image

Some additional info that might be relevant:

You can find the code for my MRE here. See the do_test function at the end. I get the following output when I run it:

hit 0
----
mContactPointOn1  = (6.736761, 0.223945, 3.262064)
mContactPointOn2  = (6.833495, -0.274887, 3.262064)
mPenetrationAxis  = (0.037024, 0.034514, -0.000000)
mPenetrationDepth = 0.499005
jrouwe commented 1 year ago

Thanks for reporting this! This sounds exactly like the problem I've been working on in the past couple of days. Can you try if the https://github.com/jrouwe/JoltPhysics/tree/bugfix/getclosest_tolerance branch fixes the issue? If it doesn't then I'll try to repro myself.

MDZhB commented 1 year ago

It seems a little harder to reproduce now, but it still happens. Using b5e2100, replace the vector at line 284 of my test program with (6.518828, 0.006255097, 4.3249197) to reposition the capsule. This produces the following output:

hit 0
----
mContactPointOn1  = (6.738268, 0.210818, 3.724920)
mContactPointOn2  = (6.833495, -0.274887, 3.724920)
mPenetrationAxis  = (0.042765, 0.039866, -0.000000)
mPenetrationDepth = 0.485805

Additionally, my unit tests picked up a change in CastShape() behaviour using b5e2100; see the following diagram. The large box has half-extents (1, 1, 1) and is positioned at (3, 0, 0). The small box has half-extents (0.5, 0.5, 0.5) and is swept from (-2, 0, 0) to (4, 0, 0). Previously, the first contact point was reported at (2, 0, 0), right in the centre of the faces in contact. Now the first contact point is reported at roughly (2, 0.48, 0.48), near one of the vertices of the smaller box.

image

The result doesn't strike me as incorrect, but it is surprising.

jrouwe commented 1 year ago

Hello,

I've been able to reproduce the first issue and can confirm that it is the same bug I've been working on, I can fix your particular example by changing the tolerance to detect degenerate triangles on this line:

https://github.com/jrouwe/JoltPhysics/blob/b5e2100995eab00eec2198c5569d0ba1e5a299af/Jolt/Geometry/ClosestPoint.h#L176

to 1.0e-11f. I can't guarantee that it will fix every situation though (maybe you can test?). I'm thinking if there's a better solution, I hate epsilons but it's hard to live without them when you use floats.

W.r.t. the second issue: Since this is a face vs face hit, any point from [2, -0.5, -0.5] - [2, 0.5, 0.5] is a valid contact point, so there is absolutely no guarantee that you will get the center of the face. If you want to know exactly how these two objects collide you can set mCollectFacesMode = CollectFaces to get the contact manifold between the two shapes.

MDZhB commented 1 year ago

Hi,

I can't guarantee that it will fix every situation though (maybe you can test?). I'm thinking if there's a better solution, I hate epsilons but it's hard to live without them when you use floats.

It is what it is. :)

I set up a few different test scenes, and so far the results look good. I'll keep my eyes peeled for similar problems.

W.r.t. the second issue: Since this is a face vs face hit, any point from [2, -0.5, -0.5] - [2, 0.5, 0.5] is a valid contact point, so there is absolutely no guarantee that you will get the center of the face. If you want to know exactly how these two objects collide you can set mCollectFacesMode = CollectFaces to get the contact manifold between the two shapes.

That's a useful tip, thanks. It's not an issue though; like I said, the result isn't wrong, it's just not the point that's intuitively representative of the contact. I mentioned it in case the change in behaviour was something you didn't want.

Thank you for looking into this so quickly.