Replies: 2 comments 4 replies
-
I added this code to the OnContactAdded function:
When running I pressed H to disable shape rendering followed by Shift+H to enable convex radius rendering: This shows that the contact points are indeed outside of the convex radius and on the edges/faces of the actual shape. But you're right that the normal in this case indeed comes from GJK which will use the rounded edges. There are several code paths here so it is not easy to explain, first it does a GJK check which uses the rounded edges (note ExcludeConvexRadius): JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.cpp Lines 82 to 92 in b1cd849 If that finds a collision inside the convex radius we don't need to run EPA so you get the normal from GJK which uses rounded edges. For shapes that have vertices (including boxes) we actually fetch the vertices from the shape to calculate an accurate contact manifold: JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.cpp Lines 145 to 151 in b1cd849 This doesn't use rounded corners so that's why in this case you do get accurate contact positions. If however one of the shapes doesn't have vertices (e.g. a sphere) it will fall back to the GJK provided contact points which would be on the rounded edges. If the contact is deep enough to be bigger than the convex radius we go to yet another code path here: JoltPhysics/Jolt/Physics/Collision/Shape/ConvexShape.cpp Lines 109 to 121 in b1cd849 Which as you can see doesn't use convex radius at all (note IncludeConvexRadius) and should return both an accurate contact point and normal. In my mind things worked a lot simpler than they actually do :) |
Beta Was this translation helpful? Give feedback.
-
In the following, I want to discuss the approach of Jolt and how it differs from other engines like PhysX or PyBullet. Some points worth mentioning; let C be a convex set:
In most implementations, however, one is required to provide two sets/geometries A and B. In this case, we have the following interpretations:
Mathematically, the necessity to provide two sets does not hurt the general case, since one can take the null-vector for B. However, it makes the discussion of GJK and EPA more cumbersome. Thus, for the following discussion, imagine we input a single set C into GJK and EPA. Why do we need a margin? The latter is well explained in [1, page 166 onwards]:
Before we continue, let us define some more sets:
In PhysX and PyBullet, the object that the physics engine works with is U + B. Here is a great video https://www.youtube.com/watch?v=BGAwRKPlpCw that visualizes that claim. The hybrid approach works like this:
Jolt, however, works a bit different. Jolt first performs a convex decomposition, that is, it tries to find a set E for which we have U = E + B. In general, this is not possible. What is possible though is to find E such that E + B is a subset of U. Note that E is defined by
So, Jolt can not define an object that it consistently works with; it can be E + B but also U. So, why does Jolt do it like this? I belive the main reasons are performance and the observation that accuracy errors are small. GJK and EPA work with polytopes. Since there is no polyope P such that P = U + B, EPA can not find the actual solution (only an approximation) for U + B and may run into numerical problems along the way. Moreover, EPA will most likely perform more iterations on a "smooth" surface than a polytope that approximates that "smooth" surface. Here, U can be seen as an approximation of E + B. Example
In the above snippet, we have Well, I hope my reasoning is correct. I greatly appreciate any feedback. [1] Collision Detection in Interactive 3D Environments by Gino Van den Bergen |
Beta Was this translation helpful? Give feedback.
-
You mentioned in godotengine/godot-proposals#7308 that:
This took me by surprise, as I was under the impression (and just took for granted) that since the convex radius of a box/cylinder/hull effectively changes the shape of it then any collision/contact normal should be affected as well.
Either I must be misunderstanding you or Jolt's API, or there is something strange going on here.
In usual fashion I've hijacked one of the tests in
Samples
(fork here), which this time happens to beContactListenerTest
, by removing itsPostPhysicsUpdate
andOnContactValidate
and changing the remaining code to be the following:With the convex radius of
box_shape
set to 0.25 like that, the normal as reported byContactManifold::mWorldSpaceNormal
for the initial impact ends up being(0.00, -0.88, 0.47)
for me. If I instead set the convex radius ofbox_shape
to be 0 then I get the expected normal of(0, 1, 0)
.The contact points do however seem to be as expected in either case, so perhaps this is just a matter of me misunderstanding the purpose of
mWorldSpaceNormal
, and I should instead calculate the normal myself by doing(point_on_2 - point_on_1).Normalized()
?EDIT: Note that this also applies when using
CollideShapeResult::mPenetrationAxis
as the contact normal.Beta Was this translation helpful? Give feedback.
All reactions