• Bullet核心类介绍(Bullet 2.82 HelloWorld程序及其详解,附程序代码)


    实验平台:win7,VS2010

    先上结果截图:

    文章最后附有生成该图的程序。

    1. 刚体模拟原理

        Bullet作为一个物理引擎,其任务就是刚体模拟(还有可变形体模拟)。刚体模拟,就是要计算预测物体的运动,举个例子,我抛一块砖头,砖头砸在地上翻了几圈最后停下来,刚体模拟就是要用计算机把这一切虚拟化(给定砖头形状质量等属性及砖头初始运动状态,还要给定地面的信息,预测砖头未来任意时刻状态)。

        刚体模拟的主要理论基础是牛顿力学(高中物理水平)。可以想见,如果刚体之间没有碰撞,刚体模拟很简单,就是自由落体计算。复杂性存在于碰撞的处理,而处理碰撞首先要检测到碰撞。碰撞检测最基本的方法就是两两刚体测试看其是否碰撞,这是不能满足效率要求的,因为每个刚体可能形状很复杂。为了进行快速碰撞检测,一般使用包围盒(Bounding Box,如AABB:Axis-Aligned Bounding Box、OBB:Oriented Bounding Box)技术,包围盒是一种简单几何体(长方体或球),刚体完全被其包含在里边。一般将碰撞检测分为两步:

    1. Broadphase Collision Detection:两两刚体,测试其包围盒是否重叠(即包围盒的碰撞检测,因为包围盒是一种简单几何体,存在快速算法处理包围盒的碰撞检测)。
    2. Narrowphase collision detection (dispatcher):对于Broadphase检测出的刚体对,进行刚体碰撞检测,任务为二,检测刚体之间是否碰撞,如果碰撞,计算出接触点(contact point)。

        这样,我们总结出,物理引擎要进行刚体模拟所要做的事(每一时间步要做的事):

    1. Broadphase Collision Detection
    2. Narrowphase collision detection
    3. 碰撞处理,由接触点及刚体属性根据物理方程计算刚体的新状态(新速度等);
    4. 更新刚体位置并输出给3D图形接口,以显示动画。

        且看Bullet为了完成刚体模拟这一复杂任务而设计的Rigid Body Physics Pipeline(刚体物理引擎管线):

    上面是Bullet的数据,下面是Bullet的刚体模拟计算步骤,对应于我们的理论分析,对照关系是这样的(管线图用红色数字标注):

    • 第1步对应管线图中:3、4;
    • 第2步对应管线图中:5;
    • 第3步对应管线图中:6;
    • 第4步对应管线图中:7、1、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包中)

      

  • 相关阅读:
    输出任意实数
    字谜游戏
    选择问题
    Spark Streaming揭秘 Day4-事务一致性(Exactly one)
    Spark Streaming揭秘 Day3-运行基石(JobScheduler)大揭秘
    Spark Streaming揭秘 Day2-五大核心特征
    Spark Streaming揭秘 Day1-三大谜团
    深度学习在美团搜索广告排序的应用实践
    美团外卖客户端高可用建设体系
    大众点评账号业务高可用进阶之路
  • 原文地址:https://www.cnblogs.com/liangliangh/p/3576353.html
Copyright © 2020-2023  润新知