forked from LeenkxTeam/LNXSDK
		
	
		
			
	
	
		
			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
 |