Bullet核心類介紹(Bullet 2.82 HelloWorld程序及其詳解,附程序代碼)

 

實驗平臺:win7,VS2010php

 

先上結果截圖:html

文章最後附有生成該圖的程序。算法

 

1. 剛體模擬原理編程

    Bullet做爲一個物理引擎,其任務就是剛體模擬(還有可變形體模擬)。剛體模擬,就是要計算預測物體的運動,舉個例子,我拋一塊磚頭,磚頭砸在地上翻了幾圈最後停下來,剛體模擬就是要用計算機把這一切虛擬化(給定磚頭形狀質量等屬性及磚頭初始運動狀態,還要給定地面的信息,預測磚頭將來任意時刻狀態)。windows

    剛體模擬的主要理論基礎是牛頓力學(高中物理水平)。能夠想見,若是剛體之間沒有碰撞,剛體模擬很簡單,就是自由落體計算。複雜性存在於碰撞的處理,而處理碰撞首先要檢測到碰撞。碰撞檢測最基本的方法就是兩兩剛體測試看其是否碰撞,這是不能知足效率要求的,由於每一個剛體可能形狀很複雜。爲了進行快速碰撞檢測,通常使用包圍盒(Bounding Box,如AABB:Axis-Aligned Bounding Box、OBB:Oriented Bounding Box)技術,包圍盒是一種簡單幾何體(長方體或球),剛體徹底被其包含在裏邊。通常將碰撞檢測分爲兩步:數組

  1. Broadphase Collision Detection:兩兩剛體,測試其包圍盒是否重疊(即包圍盒的碰撞檢測,由於包圍盒是一種簡單幾何體,存在快速算法處理包圍盒的碰撞檢測)。
  2. Narrowphase collision detection (dispatcher):對於Broadphase檢測出的剛體對,進行剛體碰撞檢測,任務爲二,檢測剛體之間是否碰撞,若是碰撞,計算出接觸點(contact point)。

    這樣,咱們總結出,物理引擎要進行剛體模擬所要作的事(每一時間步要作的事):ide

  1. Broadphase Collision Detection
  2. Narrowphase collision detection
  3. 碰撞處理,由接觸點及剛體屬性根據物理方程計算剛體的新狀態(新速度等);
  4. 更新剛體位置並輸出給3D圖形接口,以顯示動畫。

    且看Bullet爲了完成剛體模擬這一複雜任務而設計的Rigid Body Physics Pipeline(剛體物理引擎管線):函數

上面是Bullet的數據,下面是Bullet的剛體模擬計算步驟,對應於咱們的理論分析,對照關係是這樣的(管線圖用紅色數字標註):測試

  • 第1步對應管線圖中:三、4;
  • 第2步對應管線圖中:5;
  • 第3步對應管線圖中:6;
  • 第4步對應管線圖中:七、一、2;

    能夠看出,爲了實現的須要,Bullet將咱們分析的剛體模擬循環的起點改了。動畫

 

2. 對應剛體模擬幾個步驟的Bullet

  1. Bullet用btDynamicsWorld類抽象整個被模擬的世界,即btDynamicsWorld包含全部四步,另外還包含數據;
  2. 負責Broadphase Collision Detection步驟任務的類是btBroadphaseInterface
  3. 負責Narrowphase collision detection的類是btDispatcher
  4. 負責碰撞處理(約束處理)的類是btConstraintSolver
  5. 最後一步則有btDynamicsWorld類的stepSimulation方法完成;
  6. 另外表示剛體數據的類是btCollisionObject

上面介紹的類都是基類,實際完成具體任務的多是他們的子類。

 

3. 關鍵類的具體分析

首先將Bullet高層結構總結以下圖:

後面幾張圖示從Bullet API文檔中摘的,除了在線Bullet API文檔,你也能夠本身用Doxygen生成離線API文檔

DynamicsWorld

 

Broadphase

 

Dispatcher

 

ConstraintSolver

 

RigidBody

 

另外從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包中)

相關文章
相關標籤/搜索