1 /*
2 Bullet Continuous Collision Detection and Physics Library
3 Copyright (c) 2003-2006 Erwin Coumans  http://continuousphysics.com/Bullet/
4 
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising from the use of this software.
7 Permission is granted to anyone to use this software for any purpose,
8 including commercial applications, and to alter it and redistribute it freely,
9 subject to the following restrictions:
10 
11 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.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
14 */
15 
16 #include "btGjkPairDetector.h"
17 #include "BulletCollision/CollisionShapes/btConvexShape.h"
18 #include "BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h"
19 #include "BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h"
20 
21 
22 
23 #if defined(DEBUG) || defined (_DEBUG)
24 //#define TEST_NON_VIRTUAL 1
25 #include <stdio.h> //for debug printf
26 #ifdef __SPU__
27 #include <spu_printf.h>
28 #define printf spu_printf
29 //#define DEBUG_SPU_COLLISION_DETECTION 1
30 #endif //__SPU__
31 #endif
32 
33 //must be above the machine epsilon
34 #define REL_ERROR2 btScalar(1.0e-6)
35 
36 //temp globals, to improve GJK/EPA/penetration calculations
37 int gNumDeepPenetrationChecks = 0;
38 int gNumGjkChecks = 0;
39 
40 
btGjkPairDetector(const btConvexShape * objectA,const btConvexShape * objectB,btSimplexSolverInterface * simplexSolver,btConvexPenetrationDepthSolver * penetrationDepthSolver)41 btGjkPairDetector::btGjkPairDetector(const btConvexShape* objectA,const btConvexShape* objectB,btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver*	penetrationDepthSolver)
42 :m_cachedSeparatingAxis(btScalar(0.),btScalar(1.),btScalar(0.)),
43 m_penetrationDepthSolver(penetrationDepthSolver),
44 m_simplexSolver(simplexSolver),
45 m_minkowskiA(objectA),
46 m_minkowskiB(objectB),
47 m_shapeTypeA(objectA->getShapeType()),
48 m_shapeTypeB(objectB->getShapeType()),
49 m_marginA(objectA->getMargin()),
50 m_marginB(objectB->getMargin()),
51 m_ignoreMargin(false),
52 m_lastUsedMethod(-1),
53 m_catchDegeneracies(1)
54 {
55 }
btGjkPairDetector(const btConvexShape * objectA,const btConvexShape * objectB,int shapeTypeA,int shapeTypeB,btScalar marginA,btScalar marginB,btSimplexSolverInterface * simplexSolver,btConvexPenetrationDepthSolver * penetrationDepthSolver)56 btGjkPairDetector::btGjkPairDetector(const btConvexShape* objectA,const btConvexShape* objectB,int shapeTypeA,int shapeTypeB,btScalar marginA, btScalar marginB, btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver*	penetrationDepthSolver)
57 :m_cachedSeparatingAxis(btScalar(0.),btScalar(1.),btScalar(0.)),
58 m_penetrationDepthSolver(penetrationDepthSolver),
59 m_simplexSolver(simplexSolver),
60 m_minkowskiA(objectA),
61 m_minkowskiB(objectB),
62 m_shapeTypeA(shapeTypeA),
63 m_shapeTypeB(shapeTypeB),
64 m_marginA(marginA),
65 m_marginB(marginB),
66 m_ignoreMargin(false),
67 m_lastUsedMethod(-1),
68 m_catchDegeneracies(1)
69 {
70 }
71 
getClosestPoints(const ClosestPointInput & input,Result & output,class btIDebugDraw * debugDraw,bool swapResults)72 void	btGjkPairDetector::getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw,bool swapResults)
73 {
74 	(void)swapResults;
75 
76 	getClosestPointsNonVirtual(input,output,debugDraw);
77 }
78 
79 #ifdef __SPU__
getClosestPointsNonVirtual(const ClosestPointInput & input,Result & output,class btIDebugDraw * debugDraw)80 void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw)
81 #else
82 void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw)
83 #endif
84 {
85 	m_cachedSeparatingDistance = 0.f;
86 
87 	btScalar distance=btScalar(0.);
88 	btVector3	normalInB(btScalar(0.),btScalar(0.),btScalar(0.));
89 	btVector3 pointOnA,pointOnB;
90 	btTransform	localTransA = input.m_transformA;
91 	btTransform localTransB = input.m_transformB;
92 	btVector3 positionOffset = (localTransA.getOrigin() + localTransB.getOrigin()) * btScalar(0.5);
93 	localTransA.getOrigin() -= positionOffset;
94 	localTransB.getOrigin() -= positionOffset;
95 
96 	bool check2d = m_minkowskiA->isConvex2d() && m_minkowskiB->isConvex2d();
97 
98 	btScalar marginA = m_marginA;
99 	btScalar marginB = m_marginB;
100 
101 	gNumGjkChecks++;
102 
103 #ifdef DEBUG_SPU_COLLISION_DETECTION
104 	spu_printf("inside gjk\n");
105 #endif
106 	//for CCD we don't use margins
107 	if (m_ignoreMargin)
108 	{
109 		marginA = btScalar(0.);
110 		marginB = btScalar(0.);
111 #ifdef DEBUG_SPU_COLLISION_DETECTION
112 		spu_printf("ignoring margin\n");
113 #endif
114 	}
115 
116 	m_curIter = 0;
117 	int gGjkMaxIter = 1000;//this is to catch invalid input, perhaps check for #NaN?
118 	m_cachedSeparatingAxis.setValue(0,1,0);
119 
120 	bool isValid = false;
121 	bool checkSimplex = false;
122 	bool checkPenetration = true;
123 	m_degenerateSimplex = 0;
124 
125 	m_lastUsedMethod = -1;
126 
127 	{
128 		btScalar squaredDistance = BT_LARGE_FLOAT;
129 		btScalar delta = btScalar(0.);
130 
131 		btScalar margin = marginA + marginB;
132 
133 
134 
135 		m_simplexSolver->reset();
136 
137 		for ( ; ; )
138 		//while (true)
139 		{
140 
141 			btVector3 seperatingAxisInA = (-m_cachedSeparatingAxis)* input.m_transformA.getBasis();
142 			btVector3 seperatingAxisInB = m_cachedSeparatingAxis* input.m_transformB.getBasis();
143 
144 #if 1
145 
146 			btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA);
147 			btVector3 qInB = m_minkowskiB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB);
148 
149 //			btVector3 pInA  = localGetSupportingVertexWithoutMargin(m_shapeTypeA, m_minkowskiA, seperatingAxisInA,input.m_convexVertexData[0]);//, &featureIndexA);
150 //			btVector3 qInB  = localGetSupportingVertexWithoutMargin(m_shapeTypeB, m_minkowskiB, seperatingAxisInB,input.m_convexVertexData[1]);//, &featureIndexB);
151 
152 #else
153 #ifdef __SPU__
154 			btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA);
155 			btVector3 qInB = m_minkowskiB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB);
156 #else
157 			btVector3 pInA = m_minkowskiA->localGetSupportingVertexWithoutMargin(seperatingAxisInA);
158 			btVector3 qInB = m_minkowskiB->localGetSupportingVertexWithoutMargin(seperatingAxisInB);
159 #ifdef TEST_NON_VIRTUAL
160 			btVector3 pInAv = m_minkowskiA->localGetSupportingVertexWithoutMargin(seperatingAxisInA);
161 			btVector3 qInBv = m_minkowskiB->localGetSupportingVertexWithoutMargin(seperatingAxisInB);
162 			btAssert((pInAv-pInA).length() < 0.0001);
163 			btAssert((qInBv-qInB).length() < 0.0001);
164 #endif //
165 #endif //__SPU__
166 #endif
167 
168 
169 			btVector3  pWorld = localTransA(pInA);
170 			btVector3  qWorld = localTransB(qInB);
171 
172 #ifdef DEBUG_SPU_COLLISION_DETECTION
173 		spu_printf("got local supporting vertices\n");
174 #endif
175 
176 			if (check2d)
177 			{
178 				pWorld[2] = 0.f;
179 				qWorld[2] = 0.f;
180 			}
181 
182 			btVector3 w	= pWorld - qWorld;
183 			delta = m_cachedSeparatingAxis.dot(w);
184 
185 			// potential exit, they don't overlap
186 			if ((delta > btScalar(0.0)) && (delta * delta > squaredDistance * input.m_maximumDistanceSquared))
187 			{
188 				m_degenerateSimplex = 10;
189 				checkSimplex=true;
190 				//checkPenetration = false;
191 				break;
192 			}
193 
194 			//exit 0: the new point is already in the simplex, or we didn't come any closer
195 			if (m_simplexSolver->inSimplex(w))
196 			{
197 				m_degenerateSimplex = 1;
198 				checkSimplex = true;
199 				break;
200 			}
201 			// are we getting any closer ?
202 			btScalar f0 = squaredDistance - delta;
203 			btScalar f1 = squaredDistance * REL_ERROR2;
204 
205 			if (f0 <= f1)
206 			{
207 				if (f0 <= btScalar(0.))
208 				{
209 					m_degenerateSimplex = 2;
210 				} else
211 				{
212 					m_degenerateSimplex = 11;
213 				}
214 				checkSimplex = true;
215 				break;
216 			}
217 
218 #ifdef DEBUG_SPU_COLLISION_DETECTION
219 		spu_printf("addVertex 1\n");
220 #endif
221 			//add current vertex to simplex
222 			m_simplexSolver->addVertex(w, pWorld, qWorld);
223 #ifdef DEBUG_SPU_COLLISION_DETECTION
224 		spu_printf("addVertex 2\n");
225 #endif
226 			btVector3 newCachedSeparatingAxis;
227 
228 			//calculate the closest point to the origin (update vector v)
229 			if (!m_simplexSolver->closest(newCachedSeparatingAxis))
230 			{
231 				m_degenerateSimplex = 3;
232 				checkSimplex = true;
233 				break;
234 			}
235 
236 			if(newCachedSeparatingAxis.length2()<REL_ERROR2)
237             {
238 				m_cachedSeparatingAxis = newCachedSeparatingAxis;
239                 m_degenerateSimplex = 6;
240                 checkSimplex = true;
241                 break;
242             }
243 
244 			btScalar previousSquaredDistance = squaredDistance;
245 			squaredDistance = newCachedSeparatingAxis.length2();
246 #if 0
247 ///warning: this termination condition leads to some problems in 2d test case see Bullet/Demos/Box2dDemo
248 			if (squaredDistance>previousSquaredDistance)
249 			{
250 				m_degenerateSimplex = 7;
251 				squaredDistance = previousSquaredDistance;
252                 checkSimplex = false;
253                 break;
254 			}
255 #endif //
256 
257 
258 			//redundant m_simplexSolver->compute_points(pointOnA, pointOnB);
259 
260 			//are we getting any closer ?
261 			if (previousSquaredDistance - squaredDistance <= SIMD_EPSILON * previousSquaredDistance)
262 			{
263 //				m_simplexSolver->backup_closest(m_cachedSeparatingAxis);
264 				checkSimplex = true;
265 				m_degenerateSimplex = 12;
266 
267 				break;
268 			}
269 
270 			m_cachedSeparatingAxis = newCachedSeparatingAxis;
271 
272 			  //degeneracy, this is typically due to invalid/uninitialized worldtransforms for a btCollisionObject
273               if (m_curIter++ > gGjkMaxIter)
274               {
275                       #if defined(DEBUG) || defined (_DEBUG) || defined (DEBUG_SPU_COLLISION_DETECTION)
276 
277                               printf("btGjkPairDetector maxIter exceeded:%i\n",m_curIter);
278                               printf("sepAxis=(%f,%f,%f), squaredDistance = %f, shapeTypeA=%i,shapeTypeB=%i\n",
279                               m_cachedSeparatingAxis.getX(),
280                               m_cachedSeparatingAxis.getY(),
281                               m_cachedSeparatingAxis.getZ(),
282                               squaredDistance,
283                               m_minkowskiA->getShapeType(),
284                               m_minkowskiB->getShapeType());
285 
286                       #endif
287                       break;
288 
289               }
290 
291 
292 			bool check = (!m_simplexSolver->fullSimplex());
293 			//bool check = (!m_simplexSolver->fullSimplex() && squaredDistance > SIMD_EPSILON * m_simplexSolver->maxVertex());
294 
295 			if (!check)
296 			{
297 				//do we need this backup_closest here ?
298 //				m_simplexSolver->backup_closest(m_cachedSeparatingAxis);
299 				m_degenerateSimplex = 13;
300 				break;
301 			}
302 		}
303 
304 		if (checkSimplex)
305 		{
306 			m_simplexSolver->compute_points(pointOnA, pointOnB);
307 			normalInB = m_cachedSeparatingAxis;
308 			btScalar lenSqr =m_cachedSeparatingAxis.length2();
309 
310 			//valid normal
311 			if (lenSqr < 0.0001)
312 			{
313 				m_degenerateSimplex = 5;
314 			}
315 			if (lenSqr > SIMD_EPSILON*SIMD_EPSILON)
316 			{
317 				btScalar rlen = btScalar(1.) / btSqrt(lenSqr );
318 				normalInB *= rlen; //normalize
319 				btScalar s = btSqrt(squaredDistance);
320 
321 				btAssert(s > btScalar(0.0));
322 				pointOnA -= m_cachedSeparatingAxis * (marginA / s);
323 				pointOnB += m_cachedSeparatingAxis * (marginB / s);
324 				distance = ((btScalar(1.)/rlen) - margin);
325 				isValid = true;
326 
327 				m_lastUsedMethod = 1;
328 			} else
329 			{
330 				m_lastUsedMethod = 2;
331 			}
332 		}
333 
334 		bool catchDegeneratePenetrationCase =
335 			(m_catchDegeneracies && m_penetrationDepthSolver && m_degenerateSimplex && ((distance+margin) < 0.01));
336 
337 		//if (checkPenetration && !isValid)
338 		if (checkPenetration && (!isValid || catchDegeneratePenetrationCase ))
339 		{
340 			//penetration case
341 
342 			//if there is no way to handle penetrations, bail out
343 			if (m_penetrationDepthSolver)
344 			{
345 				// Penetration depth case.
346 				btVector3 tmpPointOnA,tmpPointOnB;
347 
348 				gNumDeepPenetrationChecks++;
349 				m_cachedSeparatingAxis.setZero();
350 
351 				bool isValid2 = m_penetrationDepthSolver->calcPenDepth(
352 					*m_simplexSolver,
353 					m_minkowskiA,m_minkowskiB,
354 					localTransA,localTransB,
355 					m_cachedSeparatingAxis, tmpPointOnA, tmpPointOnB,
356 					debugDraw,input.m_stackAlloc
357 					);
358 
359 
360 				if (isValid2)
361 				{
362 					btVector3 tmpNormalInB = tmpPointOnB-tmpPointOnA;
363 					btScalar lenSqr = tmpNormalInB.length2();
364 					if (lenSqr <= (SIMD_EPSILON*SIMD_EPSILON))
365 					{
366 						tmpNormalInB = m_cachedSeparatingAxis;
367 						lenSqr = m_cachedSeparatingAxis.length2();
368 					}
369 
370 					if (lenSqr > (SIMD_EPSILON*SIMD_EPSILON))
371 					{
372 						tmpNormalInB /= btSqrt(lenSqr);
373 						btScalar distance2 = -(tmpPointOnA-tmpPointOnB).length();
374 						//only replace valid penetrations when the result is deeper (check)
375 						if (!isValid || (distance2 < distance))
376 						{
377 							distance = distance2;
378 							pointOnA = tmpPointOnA;
379 							pointOnB = tmpPointOnB;
380 							normalInB = tmpNormalInB;
381 							isValid = true;
382 							m_lastUsedMethod = 3;
383 						} else
384 						{
385 							m_lastUsedMethod = 8;
386 						}
387 					} else
388 					{
389 						m_lastUsedMethod = 9;
390 					}
391 				} else
392 
393 				{
394 					///this is another degenerate case, where the initial GJK calculation reports a degenerate case
395 					///EPA reports no penetration, and the second GJK (using the supporting vector without margin)
396 					///reports a valid positive distance. Use the results of the second GJK instead of failing.
397 					///thanks to Jacob.Langford for the reproduction case
398 					///http://code.google.com/p/bullet/issues/detail?id=250
399 
400 
401 					if (m_cachedSeparatingAxis.length2() > btScalar(0.))
402 					{
403 						btScalar distance2 = (tmpPointOnA-tmpPointOnB).length()-margin;
404 						//only replace valid distances when the distance is less
405 						if (!isValid || (distance2 < distance))
406 						{
407 							distance = distance2;
408 							pointOnA = tmpPointOnA;
409 							pointOnB = tmpPointOnB;
410 							pointOnA -= m_cachedSeparatingAxis * marginA ;
411 							pointOnB += m_cachedSeparatingAxis * marginB ;
412 							normalInB = m_cachedSeparatingAxis;
413 							normalInB.normalize();
414 							isValid = true;
415 							m_lastUsedMethod = 6;
416 						} else
417 						{
418 							m_lastUsedMethod = 5;
419 						}
420 					}
421 				}
422 
423 			}
424 
425 		}
426 	}
427 
428 
429 
430 	if (isValid && ((distance < 0) || (distance*distance < input.m_maximumDistanceSquared)))
431 	{
432 #if 0
433 ///some debugging
434 //		if (check2d)
435 		{
436 			printf("n = %2.3f,%2.3f,%2.3f. ",normalInB[0],normalInB[1],normalInB[2]);
437 			printf("distance = %2.3f exit=%d deg=%d\n",distance,m_lastUsedMethod,m_degenerateSimplex);
438 		}
439 #endif
440 
441 		m_cachedSeparatingAxis = normalInB;
442 		m_cachedSeparatingDistance = distance;
443 
444 		output.addContactPoint(
445 			normalInB,
446 			pointOnB+positionOffset,
447 			distance);
448 
449 	}
450 
451 
452 }
453 
454 
455 
456 
457 
458