Differential D1739 Diff 5899 extern/bullet2/src/BulletCollision/NarrowPhaseCollision/btComputeGjkEpaPenetration.h
Changeset View
Changeset View
Standalone View
Standalone View
extern/bullet2/src/BulletCollision/NarrowPhaseCollision/btComputeGjkEpaPenetration.h
- This file was added.
| /* | |||||
| Bullet Continuous Collision Detection and Physics Library | |||||
| Copyright (c) 2003-2014 Erwin Coumans http://bulletphysics.org | |||||
| 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. | |||||
| */ | |||||
| #ifndef BT_GJK_EPA_PENETATION_CONVEX_COLLISION_H | |||||
| #define BT_GJK_EPA_PENETATION_CONVEX_COLLISION_H | |||||
| #include "LinearMath/btTransform.h" // Note that btVector3 might be double precision... | |||||
| #include "btGjkEpa3.h" | |||||
| #include "btGjkCollisionDescription.h" | |||||
| #include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" | |||||
| template <typename btConvexTemplate> | |||||
| bool btGjkEpaCalcPenDepth(const btConvexTemplate& a, const btConvexTemplate& b, | |||||
| const btGjkCollisionDescription& colDesc, | |||||
| btVector3& v, btVector3& wWitnessOnA, btVector3& wWitnessOnB) | |||||
| { | |||||
| (void)v; | |||||
| // const btScalar radialmargin(btScalar(0.)); | |||||
| btVector3 guessVector(b.getWorldTransform().getOrigin()-a.getWorldTransform().getOrigin());//?? why not use the GJK input? | |||||
| btGjkEpaSolver3::sResults results; | |||||
| if(btGjkEpaSolver3_Penetration(a,b,guessVector,results)) | |||||
| { | |||||
| // debugDraw->drawLine(results.witnesses[1],results.witnesses[1]+results.normal,btVector3(255,0,0)); | |||||
| //resultOut->addContactPoint(results.normal,results.witnesses[1],-results.depth); | |||||
| wWitnessOnA = results.witnesses[0]; | |||||
| wWitnessOnB = results.witnesses[1]; | |||||
| v = results.normal; | |||||
| return true; | |||||
| } else | |||||
| { | |||||
| if(btGjkEpaSolver3_Distance(a,b,guessVector,results)) | |||||
| { | |||||
| wWitnessOnA = results.witnesses[0]; | |||||
| wWitnessOnB = results.witnesses[1]; | |||||
| v = results.normal; | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| template <typename btConvexTemplate, typename btGjkDistanceTemplate> | |||||
| int btComputeGjkEpaPenetration(const btConvexTemplate& a, const btConvexTemplate& b, const btGjkCollisionDescription& colDesc, btVoronoiSimplexSolver& simplexSolver, btGjkDistanceTemplate* distInfo) | |||||
| { | |||||
| bool m_catchDegeneracies = true; | |||||
| btScalar m_cachedSeparatingDistance = 0.f; | |||||
| btScalar distance=btScalar(0.); | |||||
| btVector3 normalInB(btScalar(0.),btScalar(0.),btScalar(0.)); | |||||
| btVector3 pointOnA,pointOnB; | |||||
| btTransform localTransA = a.getWorldTransform(); | |||||
| btTransform localTransB = b.getWorldTransform(); | |||||
| btScalar marginA = a.getMargin(); | |||||
| btScalar marginB = b.getMargin(); | |||||
| int m_curIter = 0; | |||||
| int gGjkMaxIter = colDesc.m_maxGjkIterations;//this is to catch invalid input, perhaps check for #NaN? | |||||
| btVector3 m_cachedSeparatingAxis = colDesc.m_firstDir; | |||||
| bool isValid = false; | |||||
| bool checkSimplex = false; | |||||
| bool checkPenetration = true; | |||||
| int m_degenerateSimplex = 0; | |||||
| int m_lastUsedMethod = -1; | |||||
| { | |||||
| btScalar squaredDistance = BT_LARGE_FLOAT; | |||||
| btScalar delta = btScalar(0.); | |||||
| btScalar margin = marginA + marginB; | |||||
| simplexSolver.reset(); | |||||
| for ( ; ; ) | |||||
| //while (true) | |||||
| { | |||||
| btVector3 seperatingAxisInA = (-m_cachedSeparatingAxis)* localTransA.getBasis(); | |||||
| btVector3 seperatingAxisInB = m_cachedSeparatingAxis* localTransB.getBasis(); | |||||
| btVector3 pInA = a.getLocalSupportWithoutMargin(seperatingAxisInA); | |||||
| btVector3 qInB = b.getLocalSupportWithoutMargin(seperatingAxisInB); | |||||
| btVector3 pWorld = localTransA(pInA); | |||||
| btVector3 qWorld = localTransB(qInB); | |||||
| btVector3 w = pWorld - qWorld; | |||||
| delta = m_cachedSeparatingAxis.dot(w); | |||||
| // potential exit, they don't overlap | |||||
| if ((delta > btScalar(0.0)) && (delta * delta > squaredDistance * colDesc.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 (simplexSolver.inSimplex(w)) | |||||
| { | |||||
| m_degenerateSimplex = 1; | |||||
| checkSimplex = true; | |||||
| break; | |||||
| } | |||||
| // are we getting any closer ? | |||||
| btScalar f0 = squaredDistance - delta; | |||||
| btScalar f1 = squaredDistance * colDesc.m_gjkRelError2; | |||||
| if (f0 <= f1) | |||||
| { | |||||
| if (f0 <= btScalar(0.)) | |||||
| { | |||||
| m_degenerateSimplex = 2; | |||||
| } else | |||||
| { | |||||
| m_degenerateSimplex = 11; | |||||
| } | |||||
| checkSimplex = true; | |||||
| break; | |||||
| } | |||||
| //add current vertex to simplex | |||||
| simplexSolver.addVertex(w, pWorld, qWorld); | |||||
| btVector3 newCachedSeparatingAxis; | |||||
| //calculate the closest point to the origin (update vector v) | |||||
| if (!simplexSolver.closest(newCachedSeparatingAxis)) | |||||
| { | |||||
| m_degenerateSimplex = 3; | |||||
| checkSimplex = true; | |||||
| break; | |||||
| } | |||||
| if(newCachedSeparatingAxis.length2()<colDesc.m_gjkRelError2) | |||||
| { | |||||
| 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) | |||||
| printf("btGjkPairDetector maxIter exceeded:%i\n",m_curIter); | |||||
| printf("sepAxis=(%f,%f,%f), squaredDistance = %f\n", | |||||
| m_cachedSeparatingAxis.getX(), | |||||
| m_cachedSeparatingAxis.getY(), | |||||
| m_cachedSeparatingAxis.getZ(), | |||||
| squaredDistance); | |||||
| #endif | |||||
| break; | |||||
| } | |||||
| bool check = (!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) | |||||
| { | |||||
| 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_degenerateSimplex && ((distance+margin) < 0.01)); | |||||
| //if (checkPenetration && !isValid) | |||||
| if (checkPenetration && (!isValid || catchDegeneratePenetrationCase )) | |||||
| { | |||||
| //penetration case | |||||
| //if there is no way to handle penetrations, bail out | |||||
| // Penetration depth case. | |||||
| btVector3 tmpPointOnA,tmpPointOnB; | |||||
| m_cachedSeparatingAxis.setZero(); | |||||
| bool isValid2 = btGjkEpaCalcPenDepth(a,b, | |||||
| colDesc, | |||||
| m_cachedSeparatingAxis, tmpPointOnA, tmpPointOnB); | |||||
| 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 < colDesc.m_maximumDistanceSquared))) | |||||
| { | |||||
| m_cachedSeparatingAxis = normalInB; | |||||
| m_cachedSeparatingDistance = distance; | |||||
| distInfo->m_distance = distance; | |||||
| distInfo->m_normalBtoA = normalInB; | |||||
| distInfo->m_pointOnB = pointOnB; | |||||
| distInfo->m_pointOnA = pointOnB+normalInB*distance; | |||||
| return 0; | |||||
| } | |||||
| return -m_lastUsedMethod; | |||||
| } | |||||
| #endif //BT_GJK_EPA_PENETATION_CONVEX_COLLISION_H | |||||