internalEdgeUtility and sweep tests (character controller)

Post Reply
Lewa
Posts: 5
Joined: Wed May 31, 2017 8:55 pm

internalEdgeUtility and sweep tests (character controller)

Post by Lewa »

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:

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;
};
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)
Image



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:
Image
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;
}

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:
Image
(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?
Last edited by Lewa on Sat Jun 17, 2017 4:46 pm, edited 1 time in total.
Lewa
Posts: 5
Joined: Wed May 31, 2017 8:55 pm

Re: internalEdgeUtility and m_hitNormalLocal

Post by Lewa »

After a lot o f testing i can't seem to get this to work.

Does the internaledgeutility only work under certain circumstances? (Maybe only with dynamic Rigid Bodies?)
No matter what i do, the contanct points in the Kinematic character controller still return Edge normals which interfere with the logic of the Character controller (Slope checks detect edges of triangles on slopes which allow the character controller to climb up on them).

What i would like to do ist to return the face normal on the convex result callback.

Are there alternatives which i could look into?

/Edit:
For those looking this up in the future:
the internalEdgeUtility doesn't work with convexSweepTests because the contactAddedCallback isn't being called by this function. (It only works with regular dynamic rigidbodies and contactTest functions.) So the internaledge utility has to be called manually in this case.
Post Reply