实验平台:win7,VS2010
先上结果截图:
文章最后附有生成该图的程序。
1. 刚体模拟原理
Bullet作为一个物理引擎,其任务就是刚体模拟(还有可变形体模拟)。刚体模拟,就是要计算预测物体的运动,举个例子,我抛一块砖头,砖头砸在地上翻了几圈最后停下来,刚体模拟就是要用计算机把这一切虚拟化(给定砖头形状质量等属性及砖头初始运动状态,还要给定地面的信息,预测砖头未来任意时刻状态)。
刚体模拟的主要理论基础是牛顿力学(高中物理水平)。可以想见,如果刚体之间没有碰撞,刚体模拟很简单,就是自由落体计算。复杂性存在于碰撞的处理,而处理碰撞首先要检测到碰撞。碰撞检测最基本的方法就是两两刚体测试看其是否碰撞,这是不能满足效率要求的,因为每个刚体可能形状很复杂。为了进行快速碰撞检测,一般使用包围盒(Bounding Box,如AABB:Axis-Aligned Bounding Box、OBB:Oriented Bounding Box)技术,包围盒是一种简单几何体(长方体或球),刚体完全被其包含在里边。一般将碰撞检测分为两步:
这样,我们总结出,物理引擎要进行刚体模拟所要做的事(每一时间步要做的事):
且看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"btBulletDynamicsCommon.h" 2 #include"omp.h" 3 4 btDiscreteDynamicsWorld* m_DynamicsWorld; 5 btBroadphaseInterface* m_Broadphase; 6 btCollisionDispatcher* m_Dispatcher; 7 btSequentialImpulseConstraintSolver* m_ConstraintSolver; 8 btDefaultCollisionConfiguration* m_CollisionConfiguration; 9 btAlignedObjectArray<btCollisionShape*> m_CollisionShapes; 10 11 void bt_rundraw(bool run) 12 { 13 if(run){ 14 static double t = omp_get_wtime(); 15 double t2 = omp_get_wtime(); 16 m_DynamicsWorld->stepSimulation(float(t2-t),10); 17 t = t2; 18 } 19 20 btCollisionObjectArray& rigidArray = m_DynamicsWorld->getCollisionObjectArray(); 21 for(int i=0; i<rigidArray.size(); ++i){ 22 btRigidBody* body = btRigidBody::upcast(rigidArray[i]); 23 btTransform trans; 24 body->getMotionState()->getWorldTransform(trans); 25 float m[16]; 26 trans.getOpenGLMatrix(m); 27 GLfloat color[]={.5f, .6f, .7f, 1.0f}; 28 if(i==0){ 29 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); 30 glMatrixMode(GL_MODELVIEW); 31 glPushMatrix(); 32 glMultMatrixf(m); 33 glTranslatef(0,-1,0); 34 glScalef(100.0f,1.0f,100.0f); 35 glutSolidCube(1.f); 36 glPopMatrix(); 37 }else{ 38 if(i%2){ 39 color[0]=0.0f;color[1]=0.9f;color[2]=0.0f;color[3]=1.0f; 40 }else{ 41 color[0]=0.9f;color[1]=0.0f;color[2]=0.0f;color[3]=1.0f; 42 } 43 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); 44 glMatrixMode(GL_MODELVIEW); 45 glPushMatrix(); 46 glMultMatrixf(m); 47 glScalef(3.0f,2.0f,4.0f); 48 glutSolidCube(1.f); 49 glPopMatrix(); 50 } 51 } 52 } 53 54 void bt_start() 55 { 56 ///-----initialization_start----- 57 m_CollisionConfiguration = new btDefaultCollisionConfiguration(); 58 m_Dispatcher = new btCollisionDispatcher(m_CollisionConfiguration); 59 m_Broadphase = new btDbvtBroadphase(); 60 m_ConstraintSolver = new btSequentialImpulseConstraintSolver; 61 m_DynamicsWorld = new btDiscreteDynamicsWorld( 62 m_Dispatcher,m_Broadphase,m_ConstraintSolver,m_CollisionConfiguration); 63 m_DynamicsWorld->setGravity(btVector3(0,-10,0)); 64 ///-----initialization_end----- 65 66 { // floor 67 btCollisionShape* groundShape = new btBoxShape(btVector3(btScalar(50.f),btScalar(0.f),btScalar(50.f))); 68 m_CollisionShapes.push_back(groundShape); 69 70 btTransform groundTransform; 71 groundTransform.setIdentity(); 72 groundTransform.setOrigin(btVector3(0,0,0)); 73 btScalar mass(0.f); 74 75 btVector3 localInertia(0,0,0); 76 if( mass != 0.f ) 77 groundShape->calculateLocalInertia(mass,localInertia); 78 79 //using motionstate is recommended, it provides interpolation capabilities, and only synchronizes ‘active‘ objects 80 btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform); 81 btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,groundShape,localInertia); 82 btRigidBody* body = new btRigidBody(rbInfo); 83 84 //add the body to the dynamics world 85 m_DynamicsWorld->addRigidBody(body); 86 } 87 88 for(int i=0; i<20; ++i){ 89 btCollisionShape* boxShape = new btBoxShape(btVector3(btScalar(1.5f),btScalar(1.f),btScalar(2.f))); 90 m_CollisionShapes.push_back(boxShape); 91 92 btTransform groundTransform; 93 groundTransform.setIdentity(); 94 groundTransform.setOrigin(btVector3(0,i*2.0f+1.0f,i*0.5f)); 95 btScalar mass(6.f); 96 97 btVector3 localInertia(0,0,0); 98 if( mass != 0.f ) 99 boxShape->calculateLocalInertia(mass,localInertia); 100 101 //using motionstate is recommended, it provides interpolation capabilities, and only synchronizes ‘active‘ objects 102 btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform); 103 btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,boxShape,localInertia); 104 btRigidBody* body = new btRigidBody(rbInfo); 105 106 //add the body to the dynamics world 107 m_DynamicsWorld->addRigidBody(body); 108 } 109 110 } 111 112 void bt_end() 113 { 114 //remove the rigidbodies from the dynamics world and delete them 115 for (int i=m_DynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--) 116 { 117 btCollisionObject* obj = m_DynamicsWorld->getCollisionObjectArray()[i]; 118 btRigidBody* body = btRigidBody::upcast(obj); 119 if (body && body->getMotionState()) 120 delete body->getMotionState(); 121 m_DynamicsWorld->removeCollisionObject( obj ); 122 delete obj; 123 } 124 //delete collision shapes 125 for (int i=0;i<m_CollisionShapes.size();i++) 126 { 127 btCollisionShape* shape = m_CollisionShapes[i]; 128 m_CollisionShapes[i] = 0; 129 delete shape; 130 } 131 //delete dynamicsworld and ... 132 delete m_DynamicsWorld; 133 delete m_ConstraintSolver; 134 delete m_Broadphase; 135 delete m_Dispatcher; 136 delete m_CollisionConfiguration; 137 m_CollisionShapes.clear(); 138 }
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使用了图阴影技术绘制阴影。
另外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包中)
Bullet核心类介绍(Bullet 2.82 HelloWorld程序及其详解,附程序代码),布布扣,bubuko.com
Bullet核心类介绍(Bullet 2.82 HelloWorld程序及其详解,附程序代码)
原文:http://www.cnblogs.com/liangliangh/p/3576353.html