123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- /*
- Bullet Continuous Collision Detection and Physics Library
- Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
- This software is provided 'as-is', without any express or implied warranty.
- In no event will the authors be held liable for any damages arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it freely,
- subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
- */
- #include "btGjkPairDetector.h"
- #include "bullet/BulletCollision//CollisionShapes/btConvexShape.h"
- #include "bullet/BulletCollision//NarrowPhaseCollision/btSimplexSolverInterface.h"
- #include "bullet/BulletCollision//NarrowPhaseCollision/btConvexPenetrationDepthSolver.h"
- #if defined(DEBUG) || defined (_DEBUG)
- //#define TEST_NON_VIRTUAL 1
- #include <stdio.h> //for debug printf
- #ifdef __SPU__
- #include <spu_printf.h>
- #define printf spu_printf
- //#define DEBUG_SPU_COLLISION_DETECTION 1
- #endif //__SPU__
- #endif
- //must be above the machine epsilon
- #define REL_ERROR2 btScalar(1.0e-6)
- //temp globals, to improve GJK/EPA/penetration calculations
- int gNumDeepPenetrationChecks = 0;
- int gNumGjkChecks = 0;
- btGjkPairDetector::btGjkPairDetector(const btConvexShape* objectA,const btConvexShape* objectB,btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver* penetrationDepthSolver)
- :m_cachedSeparatingAxis(btScalar(0.),btScalar(1.),btScalar(0.)),
- m_penetrationDepthSolver(penetrationDepthSolver),
- m_simplexSolver(simplexSolver),
- m_minkowskiA(objectA),
- m_minkowskiB(objectB),
- m_shapeTypeA(objectA->getShapeType()),
- m_shapeTypeB(objectB->getShapeType()),
- m_marginA(objectA->getMargin()),
- m_marginB(objectB->getMargin()),
- m_ignoreMargin(false),
- m_lastUsedMethod(-1),
- m_catchDegeneracies(1),
- m_fixContactNormalDirection(1)
- {
- }
- btGjkPairDetector::btGjkPairDetector(const btConvexShape* objectA,const btConvexShape* objectB,int shapeTypeA,int shapeTypeB,btScalar marginA, btScalar marginB, btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver* penetrationDepthSolver)
- :m_cachedSeparatingAxis(btScalar(0.),btScalar(1.),btScalar(0.)),
- m_penetrationDepthSolver(penetrationDepthSolver),
- m_simplexSolver(simplexSolver),
- m_minkowskiA(objectA),
- m_minkowskiB(objectB),
- m_shapeTypeA(shapeTypeA),
- m_shapeTypeB(shapeTypeB),
- m_marginA(marginA),
- m_marginB(marginB),
- m_ignoreMargin(false),
- m_lastUsedMethod(-1),
- m_catchDegeneracies(1),
- m_fixContactNormalDirection(1)
- {
- }
- void btGjkPairDetector::getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw,bool swapResults)
- {
- (void)swapResults;
- getClosestPointsNonVirtual(input,output,debugDraw);
- }
- #ifdef __SPU__
- void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw)
- #else
- void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw)
- #endif
- {
- m_cachedSeparatingDistance = 0.f;
- btScalar distance=btScalar(0.);
- btVector3 normalInB(btScalar(0.),btScalar(0.),btScalar(0.));
- btVector3 pointOnA,pointOnB;
- btTransform localTransA = input.m_transformA;
- btTransform localTransB = input.m_transformB;
- btVector3 positionOffset = (localTransA.getOrigin() + localTransB.getOrigin()) * btScalar(0.5);
- localTransA.getOrigin() -= positionOffset;
- localTransB.getOrigin() -= positionOffset;
- bool check2d = m_minkowskiA->isConvex2d() && m_minkowskiB->isConvex2d();
- btScalar marginA = m_marginA;
- btScalar marginB = m_marginB;
- gNumGjkChecks++;
- #ifdef DEBUG_SPU_COLLISION_DETECTION
- spu_printf("inside gjk\n");
- #endif
- //for CCD we don't use margins
- if (m_ignoreMargin)
- {
- marginA = btScalar(0.);
- marginB = btScalar(0.);
- #ifdef DEBUG_SPU_COLLISION_DETECTION
- spu_printf("ignoring margin\n");
- #endif
- }
- m_curIter = 0;
- int gGjkMaxIter = 1000;//this is to catch invalid input, perhaps check for #NaN?
- m_cachedSeparatingAxis.setValue(0,1,0);
- bool isValid = false;
- bool checkSimplex = false;
- bool checkPenetration = true;
- m_degenerateSimplex = 0;
- m_lastUsedMethod = -1;
- {
- btScalar squaredDistance = BT_LARGE_FLOAT;
- btScalar delta = btScalar(0.);
-
- btScalar margin = marginA + marginB;
-
-
- m_simplexSolver->reset();
-
- for ( ; ; )
- //while (true)
- {
- btVector3 seperatingAxisInA = (-m_cachedSeparatingAxis)* input.m_transformA.getBasis();
- btVector3 seperatingAxisInB = m_cachedSeparatingAxis* input.m_transformB.getBasis();
- #if 1
- btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA);
- btVector3 qInB = m_minkowskiB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB);
- // btVector3 pInA = localGetSupportingVertexWithoutMargin(m_shapeTypeA, m_minkowskiA, seperatingAxisInA,input.m_convexVertexData[0]);//, &featureIndexA);
- // btVector3 qInB = localGetSupportingVertexWithoutMargin(m_shapeTypeB, m_minkowskiB, seperatingAxisInB,input.m_convexVertexData[1]);//, &featureIndexB);
- #else
- #ifdef __SPU__
- btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA);
- btVector3 qInB = m_minkowskiB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB);
- #else
- btVector3 pInA = m_minkowskiA->localGetSupportingVertexWithoutMargin(seperatingAxisInA);
- btVector3 qInB = m_minkowskiB->localGetSupportingVertexWithoutMargin(seperatingAxisInB);
- #ifdef TEST_NON_VIRTUAL
- btVector3 pInAv = m_minkowskiA->localGetSupportingVertexWithoutMargin(seperatingAxisInA);
- btVector3 qInBv = m_minkowskiB->localGetSupportingVertexWithoutMargin(seperatingAxisInB);
- btAssert((pInAv-pInA).length() < 0.0001);
- btAssert((qInBv-qInB).length() < 0.0001);
- #endif //
- #endif //__SPU__
- #endif
- btVector3 pWorld = localTransA(pInA);
- btVector3 qWorld = localTransB(qInB);
- #ifdef DEBUG_SPU_COLLISION_DETECTION
- spu_printf("got local supporting vertices\n");
- #endif
- if (check2d)
- {
- pWorld[2] = 0.f;
- qWorld[2] = 0.f;
- }
- btVector3 w = pWorld - qWorld;
- delta = m_cachedSeparatingAxis.dot(w);
- // potential exit, they don't overlap
- if ((delta > btScalar(0.0)) && (delta * delta > squaredDistance * input.m_maximumDistanceSquared))
- {
- m_degenerateSimplex = 10;
- checkSimplex=true;
- //checkPenetration = false;
- break;
- }
- //exit 0: the new point is already in the simplex, or we didn't come any closer
- if (m_simplexSolver->inSimplex(w))
- {
- m_degenerateSimplex = 1;
- checkSimplex = true;
- break;
- }
- // are we getting any closer ?
- btScalar f0 = squaredDistance - delta;
- btScalar f1 = squaredDistance * REL_ERROR2;
- if (f0 <= f1)
- {
- if (f0 <= btScalar(0.))
- {
- m_degenerateSimplex = 2;
- } else
- {
- m_degenerateSimplex = 11;
- }
- checkSimplex = true;
- break;
- }
- #ifdef DEBUG_SPU_COLLISION_DETECTION
- spu_printf("addVertex 1\n");
- #endif
- //add current vertex to simplex
- m_simplexSolver->addVertex(w, pWorld, qWorld);
- #ifdef DEBUG_SPU_COLLISION_DETECTION
- spu_printf("addVertex 2\n");
- #endif
- btVector3 newCachedSeparatingAxis;
- //calculate the closest point to the origin (update vector v)
- if (!m_simplexSolver->closest(newCachedSeparatingAxis))
- {
- m_degenerateSimplex = 3;
- checkSimplex = true;
- break;
- }
- if(newCachedSeparatingAxis.length2()<REL_ERROR2)
- {
- m_cachedSeparatingAxis = newCachedSeparatingAxis;
- m_degenerateSimplex = 6;
- checkSimplex = true;
- break;
- }
- btScalar previousSquaredDistance = squaredDistance;
- squaredDistance = newCachedSeparatingAxis.length2();
- #if 0
- ///warning: this termination condition leads to some problems in 2d test case see Bullet/Demos/Box2dDemo
- if (squaredDistance>previousSquaredDistance)
- {
- m_degenerateSimplex = 7;
- squaredDistance = previousSquaredDistance;
- checkSimplex = false;
- break;
- }
- #endif //
-
- //redundant m_simplexSolver->compute_points(pointOnA, pointOnB);
- //are we getting any closer ?
- if (previousSquaredDistance - squaredDistance <= SIMD_EPSILON * previousSquaredDistance)
- {
- // m_simplexSolver->backup_closest(m_cachedSeparatingAxis);
- checkSimplex = true;
- m_degenerateSimplex = 12;
-
- break;
- }
- m_cachedSeparatingAxis = newCachedSeparatingAxis;
- //degeneracy, this is typically due to invalid/uninitialized worldtransforms for a btCollisionObject
- if (m_curIter++ > gGjkMaxIter)
- {
- #if defined(DEBUG) || defined (_DEBUG) || defined (DEBUG_SPU_COLLISION_DETECTION)
- printf("btGjkPairDetector maxIter exceeded:%i\n",m_curIter);
- printf("sepAxis=(%f,%f,%f), squaredDistance = %f, shapeTypeA=%i,shapeTypeB=%i\n",
- m_cachedSeparatingAxis.getX(),
- m_cachedSeparatingAxis.getY(),
- m_cachedSeparatingAxis.getZ(),
- squaredDistance,
- m_minkowskiA->getShapeType(),
- m_minkowskiB->getShapeType());
- #endif
- break;
- }
- bool check = (!m_simplexSolver->fullSimplex());
- //bool check = (!m_simplexSolver->fullSimplex() && squaredDistance > SIMD_EPSILON * m_simplexSolver->maxVertex());
- if (!check)
- {
- //do we need this backup_closest here ?
- // m_simplexSolver->backup_closest(m_cachedSeparatingAxis);
- m_degenerateSimplex = 13;
- break;
- }
- }
- if (checkSimplex)
- {
- m_simplexSolver->compute_points(pointOnA, pointOnB);
- normalInB = m_cachedSeparatingAxis;
- btScalar lenSqr =m_cachedSeparatingAxis.length2();
-
- //valid normal
- if (lenSqr < 0.0001)
- {
- m_degenerateSimplex = 5;
- }
- if (lenSqr > SIMD_EPSILON*SIMD_EPSILON)
- {
- btScalar rlen = btScalar(1.) / btSqrt(lenSqr );
- normalInB *= rlen; //normalize
- btScalar s = btSqrt(squaredDistance);
-
- btAssert(s > btScalar(0.0));
- pointOnA -= m_cachedSeparatingAxis * (marginA / s);
- pointOnB += m_cachedSeparatingAxis * (marginB / s);
- distance = ((btScalar(1.)/rlen) - margin);
- isValid = true;
-
- m_lastUsedMethod = 1;
- } else
- {
- m_lastUsedMethod = 2;
- }
- }
- bool catchDegeneratePenetrationCase =
- (m_catchDegeneracies && m_penetrationDepthSolver && m_degenerateSimplex && ((distance+margin) < 0.01));
- //if (checkPenetration && !isValid)
- if (checkPenetration && (!isValid || catchDegeneratePenetrationCase ))
- {
- //penetration case
- //if there is no way to handle penetrations, bail out
- if (m_penetrationDepthSolver)
- {
- // Penetration depth case.
- btVector3 tmpPointOnA,tmpPointOnB;
-
- gNumDeepPenetrationChecks++;
- m_cachedSeparatingAxis.setZero();
- bool isValid2 = m_penetrationDepthSolver->calcPenDepth(
- *m_simplexSolver,
- m_minkowskiA,m_minkowskiB,
- localTransA,localTransB,
- m_cachedSeparatingAxis, tmpPointOnA, tmpPointOnB,
- debugDraw
- );
- if (isValid2)
- {
- btVector3 tmpNormalInB = tmpPointOnB-tmpPointOnA;
- btScalar lenSqr = tmpNormalInB.length2();
- if (lenSqr <= (SIMD_EPSILON*SIMD_EPSILON))
- {
- tmpNormalInB = m_cachedSeparatingAxis;
- lenSqr = m_cachedSeparatingAxis.length2();
- }
- if (lenSqr > (SIMD_EPSILON*SIMD_EPSILON))
- {
- tmpNormalInB /= btSqrt(lenSqr);
- 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;
- } else
- {
- m_lastUsedMethod = 8;
- }
- } else
- {
- m_lastUsedMethod = 9;
- }
- } else
- {
- ///this is another degenerate case, where the initial GJK calculation reports a degenerate case
- ///EPA reports no penetration, and the second GJK (using the supporting vector without margin)
- ///reports a valid positive distance. Use the results of the second GJK instead of failing.
- ///thanks to Jacob.Langford for the reproduction case
- ///http://code.google.com/p/bullet/issues/detail?id=250
-
- if (m_cachedSeparatingAxis.length2() > btScalar(0.))
- {
- btScalar distance2 = (tmpPointOnA-tmpPointOnB).length()-margin;
- //only replace valid distances when the distance is less
- if (!isValid || (distance2 < distance))
- {
- distance = distance2;
- pointOnA = tmpPointOnA;
- pointOnB = tmpPointOnB;
- pointOnA -= m_cachedSeparatingAxis * marginA ;
- pointOnB += m_cachedSeparatingAxis * marginB ;
- normalInB = m_cachedSeparatingAxis;
- normalInB.normalize();
- isValid = true;
- m_lastUsedMethod = 6;
- } else
- {
- m_lastUsedMethod = 5;
- }
- }
- }
-
- }
- }
- }
-
- if (isValid && ((distance < 0) || (distance*distance < input.m_maximumDistanceSquared)))
- {
- #if 0
- ///some debugging
- // if (check2d)
- {
- printf("n = %2.3f,%2.3f,%2.3f. ",normalInB[0],normalInB[1],normalInB[2]);
- printf("distance = %2.3f exit=%d deg=%d\n",distance,m_lastUsedMethod,m_degenerateSimplex);
- }
- #endif
- if (m_fixContactNormalDirection)
- {
- ///@workaround for sticky convex collisions
- //in some degenerate cases (usually when the use uses very small margins)
- //the contact normal is pointing the wrong direction
- //so fix it now (until we can deal with all degenerate cases in GJK and EPA)
- //contact normals need to point from B to A in all cases, so we can simply check if the contact normal really points from B to A
- //We like to use a dot product of the normal against the difference of the centroids,
- //once the centroid is available in the API
- //until then we use the center of the aabb to approximate the centroid
- btVector3 aabbMin,aabbMax;
- m_minkowskiA->getAabb(localTransA,aabbMin,aabbMax);
- btVector3 posA = (aabbMax+aabbMin)*btScalar(0.5);
-
- m_minkowskiB->getAabb(localTransB,aabbMin,aabbMax);
- btVector3 posB = (aabbMin+aabbMax)*btScalar(0.5);
- btVector3 diff = posA-posB;
- if (diff.dot(normalInB) < 0.f)
- normalInB *= -1.f;
- }
- m_cachedSeparatingAxis = normalInB;
- m_cachedSeparatingDistance = distance;
- output.addContactPoint(
- normalInB,
- pointOnB+positionOffset,
- distance);
- }
- }
|