Bugs in SphereTriangleDetector

yuyu
Posts: 4
Joined: Wed Jul 29, 2009 1:22 pm

Bugs in SphereTriangleDetector

Post by yuyu »

Hi there,

While debugging collisions for a btKinematicCharacter (trying to understand why I am getting a manifold that contained no contact points), I ended up in SphereTriangleDetector::collide().

Looking at the code, it was no surprise that it returned no collision in my case (a sphere resting on a plane). There is a single contact point, which is exactly on the sphere surface. However, the code does not handle that part very well:

* There is a delta vector which set to zero. However, at 2 places, we compare its dot product (with another vector) with zero. In one case it is harmless, in another, harmful (I had to comment that out).
* There is a flag (isInsideContactPlane) which takes into account the collision margin. However, a test uses another flag (isInsideShellPlane) and returns false (no collision) if the contact point isn't inside the sphere. Afterwards, we start using the first flag to search for a contact, but at that point it's too late. I had to change that.
* Moreover, these flags exclude the case of a touching contact: for example bool isInsideShellPlane = distanceFromPlane < r instead of bool isInsideShellPlane = distanceFromPlane <= r;
* There are other oddities. I am still in the process of debugging this part.

The strange thing is that when using a rigid body, this collision algorithm seemed to return correct results (as far as I can tell), albeit the oddities. Maybe because it was that the contact always happened inside the sphere. The other thing (that's why I'm still debugging) is that I believe these changes put the ConstraintSolver in an unstable state (one that is not designed to handle robustly) so my confidence in the output is low (there are some velocities set to infinite values, but we don't use them - yet? - so I didn't investigate more... However, it seems that a normal somewhere (don't know where yet) is normalized with length=0. So I end up getting an infinite normal when retrieving the manifold.

Last thing: Should a manifold even exist if it has no contact points? I thought if a there's a manifold, then it should have at least 1 point. I added an assert (numManifolds != 0) in my code but had to comment it out since it seemed that it happens quite a lot (and no, ClearManifold isn't called before ;-)).

Any thoughts on this?

Regards.

Here's the original code of the collide method (I added some comments):

-------------------------------------------
-------------------------------------------

///combined discrete/continuous sphere-triangle
bool SphereTriangleDetector::collide(const btVector3& sphereCenter,btVector3 &point, btVector3& resultNormal, btScalar& depth, btScalar &timeOfImpact, btScalar contactBreakingThreshold)
{

const btVector3* vertices = &m_triangle->getVertexPtr(0);
const btVector3& c = sphereCenter;
btScalar r = m_sphere->getRadius();

btVector3 delta (0,0,0);

btVector3 normal = (vertices[1]-vertices[0]).cross(vertices[2]-vertices[0]);
normal.normalize();
btVector3 p1ToCentre = c - vertices[0];
btScalar distanceFromPlane = p1ToCentre.dot(normal);

if (distanceFromPlane < btScalar(0.))
{
//triangle facing the other way

distanceFromPlane *= btScalar(-1.);
normal *= btScalar(-1.);
}

btScalar contactMargin = contactBreakingThreshold;
bool isInsideContactPlane = distanceFromPlane < r + contactMargin;
bool isInsideShellPlane = distanceFromPlane < r;//This should be <=

btScalar deltaDotNormal = delta.dot(normal);//delta is always zero. But ok, The second comparison in the following test is harmless
if (!isInsideShellPlane && deltaDotNormal >= btScalar(0.0))
return false;

// Check for contact / intersection
bool hasContact = false;
btVector3 contactPoint;
if (isInsideContactPlane) {//Too late at this point, we already returned false if !isInsideShellPlane
if (facecontains(c,vertices,normal)) {
// Inside the contact wedge - touches a point on the shell plane
hasContact = true;
contactPoint = c - normal*distanceFromPlane;
} else {
// Could be inside one of the contact capsules
btScalar contactCapsuleRadiusSqr = (r + contactMargin) * (r + contactMargin);
btVector3 nearestOnEdge;
for (int i = 0; i < m_triangle->getNumEdges(); i++) {

btVector3 pa;
btVector3 pb;

m_triangle->getEdge(i,pa,pb);

btScalar distanceSqr = SegmentSqrDistance(pa,pb,c, nearestOnEdge);
if (distanceSqr < contactCapsuleRadiusSqr) {
// Yep, we're inside a capsule
hasContact = true;
contactPoint = nearestOnEdge;
}

}
}
}

if (hasContact) {
btVector3 contactToCentre = c - contactPoint;
btScalar distanceSqr = contactToCentre.length2();
if (distanceSqr < (r - MAX_OVERLAP)*(r - MAX_OVERLAP)) {//Should be <=
btScalar distance = btSqrt(distanceSqr);
resultNormal = contactToCentre;
resultNormal.normalize();
point = contactPoint;
depth = -(r-distance);
return true;
}

if (delta.dot(contactToCentre) >= btScalar(0.0)) //delta is always zero, we'll always return false!
return false;

// Moving towards the contact point -> collision
point = contactPoint;
timeOfImpact = btScalar(0.0);
return true;
}

return false;
}
yuyu
Posts: 4
Joined: Wed Jul 29, 2009 1:22 pm

Re: Bugs in SphereTriangleDetector

Post by yuyu »

Some progress:

It seems that the resultNormal will only be set if the point is inside the sphere. Of course, removing this condition fixes my last major problem (normal unset), but It makes me feel very uneasy and I am not sure if the result is even valid ;-)
yuyu wrote:
if (hasContact) {
btVector3 contactToCentre = c - contactPoint;
btScalar distanceSqr = contactToCentre.length2();
if (distanceSqr < (r - MAX_OVERLAP)*(r - MAX_OVERLAP)) {//Should be <=
btScalar distance = btSqrt(distanceSqr);
resultNormal = contactToCentre;
resultNormal.normalize();
point = contactPoint;
depth = -(r-distance);
return true;
}