2 ///contribution by Pierre Terdiman to check penetration depth solvers
3 ///see http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=638
5 #ifdef WIN32//for glut.h
11 #if defined(__APPLE__) && !defined (VMDMESA)
12 #include <OpenGL/gl.h>
13 #include <OpenGL/glu.h>
14 #include <GLUT/glut.h>
24 #define VERBOSE_TEXT_ONSCREEN 1
25 #ifdef VERBOSE_TEXT_ONSCREEN
26 #include "GLDebugFont.h"
29 #include "btBulletCollisionCommon.h"
31 #include "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h"
32 #include "BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h"
33 #include "BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h"
34 #include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h"
35 #include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h"
36 #include "LinearMath/btStackAlloc.h"
38 //We can use the Bullet EPA or sampling penetration depth solver, but comparison might be useful
39 //#define COMPARE_WITH_SOLID35_AND_OTHER_EPA 1
40 #ifdef COMPARE_WITH_SOLID35_AND_OTHER_EPA
41 #include "../Extras/ExtraSolid35/Solid3EpaPenetrationDepth.h"
42 #include "../Extras/ExtraSolid35/Solid3JohnsonSimplexSolver.h"
43 #include "../Extras/EPA/EpaPenetrationDepthSolver.h"
44 #endif //COMPARE_WITH_SOLID35_AND_OTHER_EPA
46 #define USE_ORIGINAL 1
48 #include "BulletCollision/NarrowPhaseCollision/btGjkEpa2.h"
51 static bool gRefMode = false;
52 static int gMethod = 0;
53 static int gLastUsedMethod = -1;
54 static int gNumGjkIterations = -1;
55 static int gLastDegenerateSimplex = -1;
57 static const float gDisp = 0.01f;
58 static const float gCamSpeed = 0.1f;
59 static btVector3 Eye(3.0616338f, 1.1985892f, 2.5769043f);
60 static btVector3 Dir(-0.66853905,-0.14004262,-0.73037237);
64 static int glutScreenHeight = 512;
65 static int glutScreenWidth = 512;
67 static void DrawLine(const btVector3& p0, const btVector3& p1, const btVector3& color, float line_width)
69 glDisable(GL_LIGHTING);
70 glLineWidth(line_width);
71 glColor4f(color.x(), color.y(), color.z(), 1.0f);
72 btVector3 tmp[] = {p0, p1};
73 glEnableClientState(GL_VERTEX_ARRAY);
74 #ifndef BT_USE_DOUBLE_PRECISION
75 glVertexPointer(3, GL_FLOAT, sizeof(btVector3), &tmp[0].x());
77 glVertexPointer(3, GL_DOUBLE, sizeof(btVector3), &tmp[0].x());
79 glDrawArrays(GL_LINES, 0, 2);
80 glDisableClientState(GL_VERTEX_ARRAY);
81 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
82 glEnable(GL_LIGHTING);
85 void DrawTriangle(const btVector3& p0, const btVector3& p1, const btVector3& p2, const btVector3& color)
87 // glDisable(GL_LIGHTING);
88 glColor4f(color.x(), color.y(), color.z(), 1.0f);
89 btVector3 tmp[] = {p0, p1, p2};
90 glEnableClientState(GL_VERTEX_ARRAY);
91 #ifndef BT_USE_DOUBLE_PRECISION
92 glVertexPointer(3, GL_FLOAT, sizeof(btVector3), &tmp[0].x());
94 glVertexPointer(3, GL_DOUBLE, sizeof(btVector3), &tmp[0].x());
96 glDrawArrays(GL_TRIANGLES, 0, 3);
97 glDisableClientState(GL_VERTEX_ARRAY);
98 // glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
99 // glEnable(GL_LIGHTING);
105 MyPoly() : mNbVerts(0), mIndices(NULL) {}
106 ~MyPoly() { delete[]mIndices; }
119 bool LoadFromFile(const char* filename);
120 void Render(bool only_wireframe, const btVector3& wire_color) const;
121 void Project(const btVector3& dir, float& min, float& max) const;
127 btTransform mTransform;
130 MyConvex::MyConvex() :
136 mTransform.setIdentity();
139 MyConvex::~MyConvex()
145 bool MyConvex::LoadFromFile(const char* filename)
147 FILE* fp = fopen(filename, "rb");
148 if(!fp) return false;
150 fread(&mNbVerts, sizeof(int), 1, fp);
154 mVerts = new btVector3[mNbVerts];
155 for( i=0;i<mNbVerts;i++)
158 fread(vals, sizeof(float)*3, 1, fp);
159 mVerts[i].setX(vals[0]);
160 mVerts[i].setY(vals[1]);
161 mVerts[i].setZ(vals[2]);
164 fread(&mNbPolys, sizeof(int), 1, fp);
165 mPolys = new MyPoly[mNbPolys];
167 for(i=0;i<mNbPolys;i++)
169 fread(&mPolys[i].mNbVerts, sizeof(short), 1, fp);
170 mPolys[i].mIndices = new char[mPolys[i].mNbVerts];
171 fread(mPolys[i].mIndices, mPolys[i].mNbVerts, 1, fp);
172 fread(mPolys[i].mPlane, sizeof(float)*4, 1, fp);
180 //See http://www.lighthouse3d.com/opengl/glut/index.php?bmpfontortho
181 static void setOrthographicProjection()
184 // switch to projection mode
185 glMatrixMode(GL_PROJECTION);
186 // save previous matrix which contains the
187 //settings for the perspective projection
191 // set a 2D orthographic projection
192 gluOrtho2D(0, glutScreenWidth, 0, glutScreenHeight);
193 // invert the y axis, down is positive
195 // mover the origin from the bottom left corner
196 // to the upper left corner
197 glTranslatef(0, -glutScreenHeight, 0);
198 glMatrixMode(GL_MODELVIEW);
201 static void resetPerspectiveProjection()
203 glMatrixMode(GL_PROJECTION);
205 glMatrixMode(GL_MODELVIEW);
208 void MyConvex::Render(bool only_wireframe, const btVector3& wire_color) const
210 const float Scale = 1.0f;
213 ATTRIBUTE_ALIGNED16(btScalar) glmat[16]; //4x4 column major matrix for OpenGL.
215 mTransform.getOpenGLMatrix(glmat);
216 #ifndef BT_USE_DOUBLE_PRECISION
217 glMultMatrixf(&(glmat[0]));
219 glMultMatrixd(&(glmat[0]));
223 btVector3 color(0.0f, 0.5f, 1.0f);
224 for(int i=0;i<mNbPolys;i++)
226 glNormal3f(mPolys[i].mPlane[0], mPolys[i].mPlane[1], mPolys[i].mPlane[2]);
228 int NbTris = mPolys[i].mNbVerts-2;
229 const btVector3& p0 = mVerts[mPolys[i].mIndices[0]]*Scale;
230 for(int j=1;j<=NbTris;j++)
232 int k = (j+1)%mPolys[i].mNbVerts;
234 const btVector3& p1 = mVerts[mPolys[i].mIndices[j]]*Scale;
235 const btVector3& p2 = mVerts[mPolys[i].mIndices[k]]*Scale;
237 DrawTriangle(p0, p1, p2, color);
247 color = btVector3(0.0f, 0.0f, 0.0f);
249 for(int i=0;i<mNbPolys;i++)
251 for(int j=0;j<mPolys[i].mNbVerts;j++)
253 int k = (j+1)%mPolys[i].mNbVerts;
254 DrawLine(mVerts[mPolys[i].mIndices[j]]*Scale, mVerts[mPolys[i].mIndices[k]]*Scale, color, 1.0f);
262 void MyConvex::Project(const btVector3& dir, float& min, float& max) const
266 for(int i=0;i<mNbVerts;i++)
268 btVector3 pt = mTransform * mVerts[i];
269 float dp = pt.dot(dir);
270 if(dp < min) min = dp;
271 if(dp > max) max = dp;
281 static btVector3 gNormal;
282 static btVector3 gPoint;
285 struct MyResult : public btDiscreteCollisionDetectorInterface::Result
287 virtual void setShapeIdentifiersA(int partId0, int index0)
290 virtual void setShapeIdentifiersB(int partId1, int index1)
294 virtual void addContactPoint(const btVector3& normalOnBInWorld, const btVector3& pointInWorld, btScalar depth)
296 gNormal = normalOnBInWorld;
297 gPoint = pointInWorld;
302 //2 Mb by default, could be made smaller
303 btStackAlloc gStackAlloc(1024*1024*2);
305 static bool TestEPA(const MyConvex& hull0, const MyConvex& hull1)
307 static btSimplexSolverInterface simplexSolver;
308 #ifdef COMPARE_WITH_SOLID35_AND_OTHER_EPA
309 // static Solid3JohnsonSimplexSolver simplexSolver2;
310 #endif //COMPARE_WITH_SOLID35_AND_OTHER_EPA
312 simplexSolver.reset();
314 btConvexHullShape convexA((btScalar*)hull0.mVerts, hull0.mNbVerts, sizeof(btVector3));
315 btConvexHullShape convexB((btScalar*)hull1.mVerts, hull1.mNbVerts, sizeof(btVector3));
317 static btGjkEpaPenetrationDepthSolver Solver0;
318 static btMinkowskiPenetrationDepthSolver Solver1;
320 #ifdef COMPARE_WITH_SOLID35_AND_OTHER_EPA
321 static Solid3EpaPenetrationDepth Solver2;
322 static EpaPenetrationDepthSolver Solver3;
326 btConvexPenetrationDepthSolver* Solver = NULL ;
331 #ifdef COMPARE_WITH_SOLID35_AND_OTHER_EPA
336 #endif //COMPARE_WITH_SOLID35_AND_OTHER_EPA
341 btGjkPairDetector GJK(&convexA, &convexB, &simplexSolver, Solver);
342 GJK.m_catchDegeneracies = 1;
343 convexA.setMargin(0.01f);
344 convexB.setMargin(0.01f);
346 btDiscreteCollisionDetectorInterface::ClosestPointInput input;
347 input.m_transformA = hull0.mTransform;
348 input.m_transformB = hull1.mTransform;
349 input.m_stackAlloc = &gStackAlloc;
352 GJK.getClosestPoints(input, output, 0);
353 gLastUsedMethod = GJK.m_lastUsedMethod;
354 gNumGjkIterations = GJK.m_curIter;
355 gLastDegenerateSimplex= GJK.m_degenerateSimplex;
358 btVector3 witnesses[2];
362 btGjkEpaSolver::sResults results;
363 btScalar radialMargin = 0.01f;
365 btGjkEpaSolver::Collide(&convexA,hull0.mTransform,
366 &convexB,hull1.mTransform,
371 output.addContactPoint(results.normal,results.witnesses[1],-results.depth);
377 static bool TestSepAxis(const btVector3& sep_axis, const MyConvex& hull0, const MyConvex& hull1, float& depth)
381 hull0.Project(sep_axis, Min0, Max0);
382 hull1.Project(sep_axis, Min1, Max1);
384 if(Max0<Min1 || Max1<Min0)
387 float d0 = Max0 - Min1;
389 float d1 = Max1 - Min0;
391 depth = d0<d1 ? d0:d1;
395 inline bool IsAlmostZero(const btVector3& v)
397 if(fabsf(v.x())>1e-6 || fabsf(v.y())>1e-6 || fabsf(v.z())>1e-6) return false;
401 static bool ReferenceCode(const MyConvex& hull0, const MyConvex& hull1, float& dmin, btVector3& sep)
407 // Test normals from hull0
408 for( i=0;i<hull0.mNbPolys;i++)
410 btVector3 Normal(hull0.mPolys[i].mPlane[0], hull0.mPolys[i].mPlane[1], hull0.mPolys[i].mPlane[2]);
412 // btVector3 WorldNormal = hull0.mTransform * Normal;
413 btVector3 WorldNormal = hull0.mTransform.getBasis() * Normal;
416 if(!TestSepAxis(WorldNormal, hull0, hull1, d))
426 // Test normals from hull1
427 for( i=0;i<hull1.mNbPolys;i++)
429 btVector3 Normal(hull1.mPolys[i].mPlane[0], hull1.mPolys[i].mPlane[1], hull1.mPolys[i].mPlane[2]);
431 // btVector3 WorldNormal = hull1.mTransform * Normal;
432 btVector3 WorldNormal = hull1.mTransform.getBasis() * Normal;
435 if(!TestSepAxis(WorldNormal, hull0, hull1, d))
446 for(int j=0;j<hull0.mNbPolys;j++)
448 const MyPoly& poly0 = hull0.mPolys[j];
450 for(int i=0;i<hull1.mNbPolys;i++)
452 const MyPoly& poly1 = hull1.mPolys[i];
454 for(int e0=0;e0<poly0.mNbVerts;e0++)
456 const btVector3& a = hull0.mVerts[poly0.mIndices[e0]];
457 const btVector3& b = hull0.mVerts[poly0.mIndices[(e0+1)%poly0.mNbVerts]];
459 btVector3 edge0 = a - b;
460 btVector3 WorldEdge0 = hull0.mTransform.getBasis() * edge0;
462 for(int e1=0;e1<poly1.mNbVerts;e1++)
464 const btVector3& c = hull1.mVerts[poly1.mIndices[e1]];
465 const btVector3& d = hull1.mVerts[poly1.mIndices[(e1+1)%poly1.mNbVerts]];
467 btVector3 edge1 = c - d;
468 btVector3 WorldEdge1 = hull1.mTransform.getBasis() * edge1;
470 btVector3 Cross = WorldEdge0.cross(WorldEdge1);
471 if(!IsAlmostZero(Cross))
473 Cross = Cross.normalize();
476 if(!TestSepAxis(Cross, hull0, hull1, d))
493 btVector3 DeltaC = (hull1.mTransform * hull1.mTransform.getOrigin()) - (hull0.mTransform * hull0.mTransform.getOrigin());
494 if((DeltaC.dot(sep))>0.0f) sep = -sep;
501 static MyConvex gConvex0;
502 static MyConvex gConvex1;
504 static void KeyboardCallback(unsigned char key, int x, int y)
508 case 27: exit(0); break;
512 gRefMode = !gRefMode;
517 #ifdef COMPARE_WITH_SOLID35_AND_OTHER_EPA
518 if(gMethod==4) gMethod=0;
520 if(gMethod==2) gMethod=0;
525 gConvex0.mTransform.setOrigin(gConvex0.mTransform.getOrigin() + btVector3(-gDisp,0,0));
528 gConvex0.mTransform.setRotation(gConvex0.mTransform.getRotation()*btQuaternion(btVector3(1,0,0),0.01));
531 gConvex0.mTransform.setRotation(gConvex0.mTransform.getRotation()*btQuaternion(btVector3(1,0,0),-0.01));
534 gConvex0.mTransform.setRotation(gConvex0.mTransform.getRotation()*btQuaternion(btVector3(0,1,0),0.01));
537 gConvex0.mTransform.setRotation(gConvex0.mTransform.getRotation()*btQuaternion(btVector3(0,1,0),-0.01));
540 gConvex0.mTransform.setRotation(gConvex0.mTransform.getRotation()*btQuaternion(btVector3(0,0,1),0.01));
544 gConvex0.mTransform.setOrigin(gConvex0.mTransform.getOrigin() + btVector3(gDisp,0,0));
547 gConvex0.mTransform.setOrigin(gConvex0.mTransform.getOrigin() + btVector3(0,gDisp,0));
550 gConvex0.mTransform.setOrigin(gConvex0.mTransform.getOrigin() + btVector3(0,-gDisp,0));
553 case 101: Eye += Dir * gCamSpeed; break;
554 case 103: Eye -= Dir * gCamSpeed; break;
555 case 100: Eye -= N * gCamSpeed; break;
556 case 102: Eye += N * gCamSpeed; break;
560 static void ArrowKeyCallback(int key, int x, int y)
562 KeyboardCallback(key,x,y);
565 static void MouseCallback(int button, int state, int x, int y)
571 static const float NxPiF32 = 3.141592653589793f;
573 float degToRad(float a)
575 return (float)0.01745329251994329547 * a;
583 NxQuat(const float angle, const btVector3 & axis)
589 const float i_length = 1.0f / sqrtf( x*x + y*y + z*z );
594 float Half = degToRad(angle * 0.5f);
597 const float sin_theta_over_two = sinf(Half );
598 x = x * sin_theta_over_two;
599 y = y * sin_theta_over_two;
600 z = z * sin_theta_over_two;
603 void multiply(const NxQuat& left, const btVector3& right)
607 a = - left.x*right.x() - left.y*right.y() - left.z *right.z();
608 b = left.w*right.x() + left.y*right.z() - right.y()*left.z;
609 c = left.w*right.y() + left.z*right.x() - right.z()*left.x;
610 d = left.w*right.z() + left.x*right.y() - right.x()*left.y;
618 void rotate(btVector3 & v) const
627 left.multiply(*this,v);
628 float vx = left.w*myInverse.x + myInverse.w*left.x + left.y*myInverse.z - myInverse.y*left.z;
629 float vy = left.w*myInverse.y + myInverse.w*left.y + left.z*myInverse.x - myInverse.z*left.x;
630 float vz = left.w*myInverse.z + myInverse.w*left.z + left.x*myInverse.y - myInverse.x*left.y;
631 v.setValue(vx, vy, vz);
638 static void MotionCallback(int x, int y)
643 Dir = Dir.normalize();
644 N = Dir.cross(btVector3(0,1,0));
646 NxQuat qx(NxPiF32 * dx * 20/ 180.0f, btVector3(0,1,0));
648 NxQuat qy(NxPiF32 * dy * 20/ 180.0f, N);
655 static void RenderCallback()
658 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
661 glMatrixMode(GL_PROJECTION);
663 gluPerspective(60.0f, ((float)glutGet(GLUT_WINDOW_WIDTH))/((float)glutGet(GLUT_WINDOW_HEIGHT)), 1.0f, 10000.0f);
664 gluLookAt(Eye.x(), Eye.y(), Eye.z(), Eye.x() + Dir.x(), Eye.y() + Dir.y(), Eye.z() + Dir.z(), 0.0f, 1.0f, 0.0f);
666 glMatrixMode(GL_MODELVIEW);
669 glEnable(GL_LIGHTING);
671 //clear previous frames result
672 gNormal.setValue(10,0,0);
673 gPoint.setValue(0,0,0);
675 gLastUsedMethod = -1;
676 gNumGjkIterations = -1;
679 TestEPA(gConvex0, gConvex1);
680 glMatrixMode(GL_MODELVIEW);
683 btVector3 RefSep(btScalar(0.), btScalar(0.), btScalar(0.));
685 bool RefResult = false;
687 RefResult = ReferenceCode(gConvex0, gConvex1, RefDMin, RefSep);
689 // DrawLine(gPoint, gPoint + gNormal*20.0f, btVector3(1,0,0), 2.0f);
690 // printf("%f: %f %f %f\n", gDepth, gNormal.x(), gNormal.y(), gNormal.z());
692 #ifdef VERBOSE_TEXT_ONSCREEN
693 glColor3f(255.f, 255.f, 255.f);
695 setOrthographicProjection();
696 float xOffset = 10.f;
701 sprintf(buf,"gDepth=%f: gNormal=(%f %f %f)\n", gDepth, gNormal.x(), gNormal.y(), gNormal.z());
702 GLDebugDrawString(xOffset,yStart,buf);
705 sprintf(buf,"num GJK iterations =%d\n", gNumGjkIterations);
706 GLDebugDrawString(xOffset,yStart,buf);
709 sprintf(buf,"gLastUsedMethod=%d\n", gLastUsedMethod);
710 GLDebugDrawString(xOffset,yStart,buf);
717 if (gLastUsedMethod >= 3)
722 sprintf(buf,"Bullet GjkEpa Penetration depth solver (zlib free\n" );
725 sprintf(buf,"Bullet Minkowski sampling Penetration depth solver\n" );
728 sprintf(buf,"Solid35 EPA Penetration depth solver\n" );
731 sprintf(buf,"EPA Penetration depth solver (Experimental/WorkInProgress, zlib free\n" );
734 sprintf(buf,"Unknown Penetration Depth\n" );
736 GLDebugDrawString(xOffset,yStart,buf);
741 sprintf(buf,"Hybrid GJK method %d\n", gLastUsedMethod);
742 GLDebugDrawString(xOffset,yStart,buf);
746 if (gLastDegenerateSimplex)
748 sprintf(buf,"DegenerateSimplex %d\n", gLastDegenerateSimplex);
749 GLDebugDrawString(xOffset,yStart,buf);
756 resetPerspectiveProjection();
757 #endif //VERBOSE_TEXT_ONSCREEN
759 btVector3 color(0,0,0);
760 gConvex0.Render(false, color);
761 gConvex1.Render(false, color);
765 btTransform Saved = gConvex0.mTransform;
766 gConvex0.mTransform.setOrigin(gConvex0.mTransform.getOrigin() - btVector3(gNormal*gDepth));
767 gConvex0.Render(true, btVector3(1.0f, 0.5f, 0.0f));
768 gConvex0.mTransform = Saved;
772 DrawLine(gPoint, gPoint + gNormal, btVector3(0,1,0), 2.0f);
775 if(RefResult & gRefMode)
777 btTransform Saved = gConvex0.mTransform;
778 gConvex0.mTransform.setOrigin(gConvex0.mTransform.getOrigin() + btVector3(RefSep*RefDMin));
779 gConvex0.Render(true, btVector3(0.0f, 0.5f, 1.0f));
780 gConvex0.mTransform = Saved;
786 static void ReshapeCallback(int width, int height)
788 glViewport(0, 0, width, height);
791 static void IdleCallback()
796 int main(int argc, char** argv)
799 glutInit(&argc, argv);
800 glutInitWindowSize(glutScreenWidth, glutScreenHeight);
801 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
802 int mainHandle = glutCreateWindow("TestBullet");
803 glutSetWindow(mainHandle);
804 glutDisplayFunc(RenderCallback);
805 glutReshapeFunc(ReshapeCallback);
806 glutIdleFunc(IdleCallback);
807 glutKeyboardFunc(KeyboardCallback);
808 glutSpecialFunc(ArrowKeyCallback);
809 glutMouseFunc(MouseCallback);
810 glutMotionFunc(MotionCallback);
813 // Setup default render states
814 glClearColor(0.3f, 0.4f, 0.5f, 1.0);
815 glEnable(GL_DEPTH_TEST);
816 glEnable(GL_COLOR_MATERIAL);
817 glEnable(GL_CULL_FACE);
820 glEnable(GL_LIGHTING);
821 float AmbientColor[] = { 0.0f, 0.1f, 0.2f, 0.0f }; glLightfv(GL_LIGHT0, GL_AMBIENT, AmbientColor);
822 float DiffuseColor[] = { 1.0f, 1.0f, 1.0f, 0.0f }; glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseColor);
823 float SpecularColor[] = { 0.0f, 0.0f, 0.0f, 0.0f }; glLightfv(GL_LIGHT0, GL_SPECULAR, SpecularColor);
824 float Position[] = { -10.0f, 1000.0f, -4.0f, 1.0f }; glLightfv(GL_LIGHT0, GL_POSITION, Position);
828 bool Status = gConvex0.LoadFromFile("convex0.bin");
831 Status = gConvex0.LoadFromFile("../../convex0.bin");
834 printf("Failed to load object!\n");
838 Status = gConvex1.LoadFromFile("convex0.bin");
841 Status = gConvex1.LoadFromFile("../../convex0.bin");
844 printf("Failed to load object!\n");
849 // gConvex0.mTransform.setOrigin(btVector3(1.0f, 1.0f, 0.0f));
850 gConvex0.mTransform.setOrigin(btVector3(0.20000069f, 0.95000005f, 0.0f));