370 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			370 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| 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
 |