internalEdgeUtility and sweep tests (character controller)
Posted: Wed May 31, 2017 10:19 pm
So, i recently ran into the issue with bullets kinematic character controller on slopes.
The character controller uses a convexSweepTest with a callback which can determine if the character is standing on a slope and how steep the slope is.
This is the callback:
It works for the most part, but i noticed that if you slide down a slope (the geometry is made up of a triangleMesh) it can get "stuck" on the exact point where two triangles intersect (on the edges.)
This is the scenario: (the blue and red line is a plane made out of triangles show from the side)
I was able to track the issue down to the "convexResult.m_hitNormalLocal" which seems to differ from the triangleface normal.
Here is an illustration of the issue:
This normal causes the dotproduct to return an incorrect value which in turn stops the sliding effect.
The character controller is basically able to walk up on those slopes by using the edge intersections.
After a lot of research i came across the internalEdgeUtility which (as far as i was able to gather from various sources) should fix this issue by replacing the normals with the triangle face normal.
So i implemented this utility into my code (set the flag for the triangle mesh correctly, created a triangleinfomap, etc...) and added a contactaddedCallback which looks like this:
And, it seems to work. The console output shows that the "bad" normal seems to be correctly set to the triangle face normal.
But! the "convexResult.m_hitNormalLocal" in the sweeptest callback seems to ignore this? (It still returns the unwanted normal.)
This is the console output:
(The green and orange/yellow result is the debug output of the m_hitNormalLocal from the convexSweepTest callback in the character controller. you can see the debug output above the "if (dotUp < m_minSlopeDot) {" line in the callback at the top of this post)
Notice how the "correct" normal has (mostly) the exact same value as the fixed (face) normals.
But the callback (orange) returns one contactpointManifold which still seems to have the wrong normal. (Notice how the orange marked normal has the same values as the "BEFORE" result from the Triangle 2.)
So my question is:
Are the hitnormals not affected by the internaledgeutility? Am i missing something here?
The character controller uses a convexSweepTest with a callback which can determine if the character is standing on a slope and how steep the slope is.
This is the callback:
Code: Select all
class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
{
public:
btKinematicClosestNotMeConvexResultCallback(btCollisionObject* me, const btVector3& up, btScalar minSlopeDot)
: btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
, m_me(me)
, m_up(up)
, m_minSlopeDot(minSlopeDot)
{
}
virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
{
if (convexResult.m_hitCollisionObject == m_me)
return btScalar(1.0);
if (!convexResult.m_hitCollisionObject->hasContactResponse())
return btScalar(1.0);
btVector3 hitNormalWorld;
if (normalInWorldSpace)
{
hitNormalWorld = convexResult.m_hitNormalLocal;
}
else
{
///need to transform normal into worldspace
hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal;
}
btScalar dotUp = m_up.dot(hitNormalWorld);
Engine::Debug::println(std::to_string(hitNormalWorld.getX()) + " / " + std::to_string(hitNormalWorld.getY()) + " / " + std::to_string(hitNormalWorld.getZ()) + " / ");
if (dotUp < m_minSlopeDot) {
return btScalar(1.0);
}
return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
}
protected:
btCollisionObject* m_me;
const btVector3 m_up;
btScalar m_minSlopeDot;
};
This is the scenario: (the blue and red line is a plane made out of triangles show from the side)
I was able to track the issue down to the "convexResult.m_hitNormalLocal" which seems to differ from the triangleface normal.
Here is an illustration of the issue:
This normal causes the dotproduct to return an incorrect value which in turn stops the sliding effect.
The character controller is basically able to walk up on those slopes by using the edge intersections.
After a lot of research i came across the internalEdgeUtility which (as far as i was able to gather from various sources) should fix this issue by replacing the normals with the triangle face normal.
So i implemented this utility into my code (set the flag for the triangle mesh correctly, created a triangleinfomap, etc...) and added a contactaddedCallback which looks like this:
Code: Select all
static bool CustomMaterialCombinerCallback(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper* colObj1Wrap, int partId1, int index1)
{
//Debug stuff. ignore
btVector3 pA = cp.m_normalWorldOnB;
btVector3 a = cp.m_normalWorldOnB;
//TODO: Proper check (if colObj1wrap shape is triangle, etc...)
btAdjustInternalEdgeContacts(cp, colObj1Wrap, colObj0Wrap, partId1, index1);
btAdjustInternalEdgeContacts(cp, colObj0Wrap, colObj1Wrap, partId0, index0);
//debug stuff
btVector3 pB = cp.m_normalWorldOnB;
a = a - cp.m_normalWorldOnB;
if (a.length() > 0) {
Engine::Debug::println("BEFORE> "+ std::to_string(pA.getX()) + " / " + std::to_string(pA.getY()) + " / " + std::to_string(pA.getZ()) + " / ");
Engine::Debug::println("AFTER> " + std::to_string(pB.getX()) + " / " + std::to_string(pB.getY()) + " / " + std::to_string(pB.getZ()) + " / ");
Engine::Debug::println("----------------------");
}
return true;
}
But! the "convexResult.m_hitNormalLocal" in the sweeptest callback seems to ignore this? (It still returns the unwanted normal.)
This is the console output:
(The green and orange/yellow result is the debug output of the m_hitNormalLocal from the convexSweepTest callback in the character controller. you can see the debug output above the "if (dotUp < m_minSlopeDot) {" line in the callback at the top of this post)
Notice how the "correct" normal has (mostly) the exact same value as the fixed (face) normals.
But the callback (orange) returns one contactpointManifold which still seems to have the wrong normal. (Notice how the orange marked normal has the same values as the "BEFORE" result from the Triangle 2.)
So my question is:
Are the hitnormals not affected by the internaledgeutility? Am i missing something here?