實驗平臺:win7,VS2010php
先上結果截圖:html
文章最後附有生成該圖的程序。算法
1. 剛體模擬原理編程
Bullet做爲一個物理引擎,其任務就是剛體模擬(還有可變形體模擬)。剛體模擬,就是要計算預測物體的運動,舉個例子,我拋一塊磚頭,磚頭砸在地上翻了幾圈最後停下來,剛體模擬就是要用計算機把這一切虛擬化(給定磚頭形狀質量等屬性及磚頭初始運動狀態,還要給定地面的信息,預測磚頭將來任意時刻狀態)。windows
剛體模擬的主要理論基礎是牛頓力學(高中物理水平)。能夠想見,若是剛體之間沒有碰撞,剛體模擬很簡單,就是自由落體計算。複雜性存在於碰撞的處理,而處理碰撞首先要檢測到碰撞。碰撞檢測最基本的方法就是兩兩剛體測試看其是否碰撞,這是不能知足效率要求的,由於每一個剛體可能形狀很複雜。爲了進行快速碰撞檢測,通常使用包圍盒(Bounding Box,如AABB:Axis-Aligned Bounding Box、OBB:Oriented Bounding Box)技術,包圍盒是一種簡單幾何體(長方體或球),剛體徹底被其包含在裏邊。通常將碰撞檢測分爲兩步:數組
這樣,咱們總結出,物理引擎要進行剛體模擬所要作的事(每一時間步要作的事):ide
且看Bullet爲了完成剛體模擬這一複雜任務而設計的Rigid Body Physics Pipeline(剛體物理引擎管線):函數
上面是Bullet的數據,下面是Bullet的剛體模擬計算步驟,對應於咱們的理論分析,對照關係是這樣的(管線圖用紅色數字標註):測試
能夠看出,爲了實現的須要,Bullet將咱們分析的剛體模擬循環的起點改了。動畫
2. 對應剛體模擬幾個步驟的Bullet類
上面介紹的類都是基類,實際完成具體任務的多是他們的子類。
3. 關鍵類的具體分析
首先將Bullet高層結構總結以下圖:
後面幾張圖示從Bullet API文檔中摘的,除了在線Bullet API文檔,你也能夠本身用Doxygen生成離線API文檔。
另外從btDynamicsWorld類的合做圖能夠看出上述分析的正確性:
如上圖紅圈所示,btDynamicsWorld中包含了(或者說指向了)Broadphase、Dispatcher、ConstraintSolver、RigidBodys(多個,RigidBody數組)。
4. Bullet 2.82 HelloWorld程序
代碼以下:
1 #include"GL/glew.h" 2 #include"GL/freeglut.h" 3 #include"btBulletDynamicsCommon.h" 4 #include"omp.h" 5 6 btDiscreteDynamicsWorld* m_DynamicsWorld; 7 btBroadphaseInterface* m_Broadphase; 8 btCollisionDispatcher* m_Dispatcher; 9 btSequentialImpulseConstraintSolver* m_ConstraintSolver; 10 btDefaultCollisionConfiguration* m_CollisionConfiguration; 11 btAlignedObjectArray<btCollisionShape*> m_CollisionShapes; 12 13 void bt_rundraw(bool run) 14 { 15 static double t_Last = omp_get_wtime(); 16 if(run){ 17 double t2 = omp_get_wtime(); 18 m_DynamicsWorld->stepSimulation(float(t2-t_Last),10); 19 t_Last = t2; 20 }else{ 21 t_Last = omp_get_wtime(); 22 } 23 24 btCollisionObjectArray& rigidArray = m_DynamicsWorld->getCollisionObjectArray(); 25 for(int i=0; i<rigidArray.size(); ++i){ 26 btRigidBody* body = btRigidBody::upcast(rigidArray[i]); 27 btTransform trans; 28 body->getMotionState()->getWorldTransform(trans); 29 float m[16]; 30 trans.getOpenGLMatrix(m); 31 GLfloat color[]={.5f, .6f, .7f, 1.0f}; 32 if(i==0){ 33 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); 34 glMatrixMode(GL_MODELVIEW); 35 glPushMatrix(); 36 glMultMatrixf(m); 37 glTranslatef(0,-1,0); 38 glScalef(100.0f,1.0f,100.0f); 39 glutSolidCube(1.f); 40 glPopMatrix(); 41 }else{ 42 if(i%2){ 43 color[0]=0.0f;color[1]=0.9f;color[2]=0.0f;color[3]=1.0f; 44 }else{ 45 color[0]=0.9f;color[1]=0.0f;color[2]=0.0f;color[3]=1.0f; 46 } 47 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); 48 glMatrixMode(GL_MODELVIEW); 49 glPushMatrix(); 50 glMultMatrixf(m); 51 glScalef(3.0f,2.0f,4.0f); 52 glutSolidCube(1.f); 53 glPopMatrix(); 54 } 55 } 56 } 57 58 void bt_start() 59 { 60 ///-----initialization_start----- 61 m_CollisionConfiguration = new btDefaultCollisionConfiguration(); 62 m_Dispatcher = new btCollisionDispatcher(m_CollisionConfiguration); 63 m_Broadphase = new btDbvtBroadphase(); 64 m_ConstraintSolver = new btSequentialImpulseConstraintSolver; 65 m_DynamicsWorld = new btDiscreteDynamicsWorld( 66 m_Dispatcher,m_Broadphase,m_ConstraintSolver,m_CollisionConfiguration); 67 m_DynamicsWorld->setGravity(btVector3(0,-10,0)); 68 ///-----initialization_end----- 69 70 { // floor 71 btCollisionShape* groundShape = new btBoxShape(btVector3(btScalar(50.f),btScalar(0.f),btScalar(50.f))); 72 m_CollisionShapes.push_back(groundShape); 73 74 btTransform groundTransform; 75 groundTransform.setIdentity(); 76 groundTransform.setOrigin(btVector3(0,0,0)); 77 btScalar mass(0.f); 78 79 btVector3 localInertia(0,0,0); 80 if( mass != 0.f ) 81 groundShape->calculateLocalInertia(mass,localInertia); 82 83 //using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects 84 btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform); 85 btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,groundShape,localInertia); 86 btRigidBody* body = new btRigidBody(rbInfo); 87 88 //add the body to the dynamics world 89 m_DynamicsWorld->addRigidBody(body); 90 } 91 92 for(int i=0; i<20; ++i){ 93 btCollisionShape* boxShape = new btBoxShape(btVector3(btScalar(1.5f),btScalar(1.f),btScalar(2.f))); 94 m_CollisionShapes.push_back(boxShape); 95 96 btTransform groundTransform; 97 groundTransform.setIdentity(); 98 groundTransform.setOrigin(btVector3(0,i*2.0f+1.0f,i*0.5f)); 99 btScalar mass(6.f); 100 101 btVector3 localInertia(0,0,0); 102 if( mass != 0.f ) 103 boxShape->calculateLocalInertia(mass,localInertia); 104 105 //using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects 106 btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform); 107 btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,boxShape,localInertia); 108 btRigidBody* body = new btRigidBody(rbInfo); 109 110 //add the body to the dynamics world 111 m_DynamicsWorld->addRigidBody(body); 112 } 113 114 } 115 116 void bt_end() 117 { 118 //remove the rigidbodies from the dynamics world and delete them 119 for (int i=m_DynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--) 120 { 121 btCollisionObject* obj = m_DynamicsWorld->getCollisionObjectArray()[i]; 122 btRigidBody* body = btRigidBody::upcast(obj); 123 if (body && body->getMotionState()) 124 delete body->getMotionState(); 125 m_DynamicsWorld->removeCollisionObject( obj ); 126 delete obj; 127 } 128 //delete collision shapes 129 for (int i=0;i<m_CollisionShapes.size();i++) 130 { 131 btCollisionShape* shape = m_CollisionShapes[i]; 132 m_CollisionShapes[i] = 0; 133 delete shape; 134 } 135 //delete dynamicsworld and ... 136 delete m_DynamicsWorld; 137 delete m_ConstraintSolver; 138 delete m_Broadphase; 139 delete m_Dispatcher; 140 delete m_CollisionConfiguration; 141 m_CollisionShapes.clear(); 142 }
bt_start()函數中構建DynamicsWorld,包括Broadphase、Dispatcher、ConstraintSolver、RigidBodys。Bullet的設計原則是:誰new對象,誰就負責delete它,因此在bt_end()函數中delete全部new出來的對象。bt_rundraw()函數調用btDiscreteDynamicsWorld:: stepSimulation()步進模擬時間,並用OpenGL繪製所模擬的物體。該程序用到了OpenMP庫的時間函數,參見:OpenMP共享內存並行編程總結表。
bt_start()、bt_end()、bt_rundraw()的使用方法是:在初始化代碼中調用bt_start(),在模擬完成(動畫結束)後調用bt_end()釋放資源,在繪製每幀時調用bt_rundraw()。
讀者也能夠看看Bullet Demo中的App_BasicDemo項目,這裏指出App_BasicDemo項目中和Bullet相關代碼的地方:和bt_start()對應的代碼在BasicDemo::initPhysics()(BasicDemo.cpp文件116行);和bt_end()對應的代碼在BasicDemo::exitPhysics()(BasicDemo.cpp文件231行);和bt_rundraw()對應的代碼在BasicDemo::clientMoveAndDisplay()(BasicDemo.cpp文件64行),具體OpenGL繪製代碼在父類裏,就不細說了,能夠看到,Bullet Demo使用了陰影體技術(Shadow Volumes)繪製陰影。
另外Bullet官網也有教程解釋HelloWorld程序,見參考文獻所列的連接。
考慮到方便本文的讀者作實驗,將程序共享出來,程序寫的甚是簡陋,請輕拍:
連接:http://pan.baidu.com/share/link?shareid=851836958&uk=2299460138 密碼:k8sj
能夠拖拽鼠標調整視角,滾動滾輪縮放,按鍵盤r鍵開始動畫,OpenGL程序配置見個人另外一篇文章:配置本身的OpenGL庫,glew、freeglut庫編譯,庫衝突解決(附OpenGL Demo程序)。Bullet的編譯安裝見:windows下Bullet 2.82編譯安裝(Bullet Physics開發環境配置)。
參考文獻:
Bullet 2.82 Physics SDK Manual(在下載的Bullet包中)
http://bulletphysics.org/mediawiki-1.5.8/index.php/Hello_World
Bullet Demo App_BasicDemo(在下載的Bullet包中)