in btGjkEpa2.cpp around line 219 (in bullet-2.79) is a check whether the length of a vector is "zero".
this is implemented like this:
Code: Select all
const btScalar rl=m_ray.length();
if(rl< GJK_MIN_DISTANCE)
Code: Select all
#define GJK_MIN_DISTANCE ((btScalar)0.0001)
i have a body->setMargin(0.001) -> that means one millimeter for me.
the effect i'm observing is like this:
- one bodyA is slowly approaching another bodyB
- they are so close that the NarrowPhase collision detection is running in each step.
- bodies are checked for penetration in btGjkPairDetector with m_penetrationDepthSolver->calcPenDepth()
- gjkepa2_impl::GJK::Evaluate behind this calcPenDepth() exits with the if-statement above and a rl.length() beeing 0.000073...
- gjk_status is then set to GJK::eStatus::Inside but EPA::Evaluate() returns a epa.m_depth of EXACTLY 0 !!
-- i think this should not happen! the bodies are not really touching each other yet
- epa.m_depth beeing exactly 0 also causes the two reported world contact points on each body to be EXACTLY the same:
Code: Select all
results.witnesses[0] = wtrs0*w0;
results.witnesses[1] = wtrs0*(w0-epa.m_normal*epa.m_depth);
Code: Select all
btScalar distance2 = -(tmpPointOnA-tmpPointOnB).length();
//only replace valid penetrations when the result is deeper (check)
if (!isValid || (distance2 < distance))
{
distance = distance2;
pointOnA = tmpPointOnA;
pointOnB = tmpPointOnB;
normalInB = tmpNormalInB;
isValid = true;
m_lastUsedMethod = 3;
- at the end of btGjkPairDetector::getClosestPointsNonVirtual() this penetrating contact point is reported:
Code: Select all
m_cachedSeparatingAxis = normalInB;
m_cachedSeparatingDistance = distance;
output.addContactPoint(
normalInB,
pointOnB+positionOffset,
distance);
gjkPairDetector.getClosestPoints() returns with this 0-contact from above.
Code: Select all
gjkPairDetector.getClosestPoints(input,dummy,dispatchInfo.m_debugDraw);
btScalar l2 = gjkPairDetector.getCachedSeparatingAxis().length2();
if (l2>SIMD_EPSILON)
{
sepNormalWorldSpace = gjkPairDetector.getCachedSeparatingAxis()*(1.f/l2);
//minDist = -1e30f;//gjkPairDetector.getCachedSeparatingDistance();
minDist = gjkPairDetector.getCachedSeparatingDistance()-min0->getMargin()-min1->getMargin();
foundSepAxis = gjkPairDetector.getCachedSeparatingDistance()<(min0->getMargin()+min1->getMargin());
Code: Select all
minDist = -0 - min0->getMargin() - min1->getMargin();
Code: Select all
minDist = -0.002

a few lines below we have
Code: Select all
btPolyhedralContactClipping::clipHullAgainstHull(sepNormalWorldSpace, *polyhedronA->getConvexPolyhedron(), *polyhedronB->getConvexPolyhedron(),
body0->getWorldTransform(),
body1->getWorldTransform(), minDist-threshold, threshold, *resultOut);
(-0.002 - threshold) (with threshold NOT beeing a constant this time -- its a relatively small computed value of 0.000678... in this case. minDist is 0.0026782261835028051
btPolyhedralContactClipping::clipFaceAgainstHull() also gets this parameter. in there this contact gets added to
btDiscreteCollisionDetectorInterface::Result& resultOut here:
Code: Select all
// only keep points that are behind the witness face
{
btVector3 localPlaneNormal (polyA.m_plane[0],polyA.m_plane[1],polyA.m_plane[2]);
btScalar localPlaneEq = polyA.m_plane[3];
btVector3 planeNormalWS = transA.getBasis()*localPlaneNormal;
btScalar planeEqWS=localPlaneEq-planeNormalWS.dot(transA.getOrigin());
for (int i=0;i<pVtxIn->size();i++)
{
btScalar depth = planeNormalWS.dot(pVtxIn->at(i))+planeEqWS;
if (depth <=minDist)
{
// printf("clamped: depth=%f to minDist=%f\n",depth,minDist);
depth = minDist;
}
if (depth <=maxDist)
{
btVector3 point = pVtxIn->at(i);
#ifdef ONLY_REPORT_DEEPEST_POINT
curMaxDist = depth;
#else
#if 0
if (depth<-3)
{
printf("error in btPolyhedralContactClipping depth = %f\n", depth);
printf("likely wrong separatingNormal passed in\n");
}
#endif
resultOut.addContactPoint(separatingNormal,point,depth);
#endif
}
}
}
voilla: i get a sudden penetration of 2.6 millimeters which causes the bodies to be thrown apart in a really ugly manner.
attached is a small example program which reproduces this very deterministically.... (start it with -high as commandline argument or set start_high = true)
and here you can see these sudden "deep" penetrating contacts:
ok, so i think the root of this problem is EPA::Evaluate() exiting in its fallback-case:
Code: Select all
/* Fallback */
m_status = eStatus::FallBack;
m_normal = -guess;
const btScalar nl=m_normal.length();
if(nl>0)
m_normal = m_normal/nl;
else
m_normal = btVector3(1,0,0);
m_depth = 0;
m_result.rank=1;
m_result.c[0]=simplex.c[0];
m_result.p[0]=1;
return(m_status);
probably EPA::Evaluate() goes thru this fallback because GJK::Evaluate() reports a "strange"/not real contact because of that too rough zero checking constant.
another funny side note to this: if i choose the start position of the bodies to be very close, then GJK will not run in this problem and no wrong penetrations are generated. you can test this with the test-program run with start_high = false.