关于学习Bullet的初衷是想在我的试验中加入更可信的三维碰撞检测机制,在这么多物理库的选择的为什么选择Bullet的主要原因是它的开源性以及支持IOS,其实这是我的肤浅的认识,关于Bullet、Physx、Havok、Box2D等优缺点网上有许多较深入的理解的文章,这里就不列举了。
1、Bullet的下载与配置
参考文章:http://blog.csdn.net/dhull/article/details/20463563
1 Bullet安装指导
- 下载bullet-2.82-r2704.zip,解压(r后面的数字可能不同);
- 运行“../build/vs2010.bat”,生成VS2010工程;
- 用VS2010打开“../vs2010/0BulletSolution.sln”;
- 对LinearMath、BulletCollision、BulletDynamics、BulletSoftBody,分别在Debug和Release配置下编译生成LIB;
- 将“../lib”下的8个.lib文件,以及“../src”下所有.h文件,都拷贝出来即构成安装包;其中,.lib文件放到lib文件夹先,.h文件放到inc文件夹下。
注:生成lib也可参考http://bulletphysics.org/mediawiki-1.5.8/index.php/Creating_a_project_from_scratch,这里有详细的图文讲解2 Bullet工程配置(使用Bullet库)
要使用Bullet,需要:
- 添加包含目录,将上一节拷贝的.h文件所在目录添加到工程的包含目录(在所有配置下);
- 添加库目录,将上一节拷贝的.lib文件所在目录添加到工程库目录(在所有配置下);
- 引用库,添加对.lib文件的引用(根据使用层次添加所需.lib文件,Debug和Release分别设置);
- 包含头文件,在代码中包含“btBulletDynamicsCommon.h”。
- 接下来,就可以自己编辑代码了。
注:
添加包含目录的两种方法:
- “项目属性 >> 配置属性 >> VC++目录 >> 包含目录”
- “项目属性 >> 配置属性 >> C/C++ >> 常规 >> 附加包含目录”。
添加库目录的两种方法:
- “项目属性 >> 配置属性 >> VC++目录 >> 库目录”
- “项目属性 >> 配置属性 >> 链接器 >> 常规 >> 附加库目录”。
引用库的两种方法:
- 代码#pragma comment (lib, "xxx.lib")(用#ifdef _DLL和#ifdef _DEBUG);
- “项目属性 >> 配置属性 >> 链接器 >> 输入 >> 附加依赖项”。
2、Bullet的HelloWord示例
#include <iostream>
#include <btBulletDynamicsCommon.h>
int main (void)
{
btBroadphaseInterface* broadphase = new btDbvtBroadphase();
btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration();
btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);
btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
dynamicsWorld->setGravity(btVector3(0,-10,0));
btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0,1,0),1);
btCollisionShape* fallShape = new btSphereShape(1);
btDefaultMotionState* groundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,-1,0)));
btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI(0,groundMotionState,groundShape,btVector3(0,0,0));
btRigidBody* groundRigidBody = new btRigidBody(groundRigidBodyCI);
dynamicsWorld->addRigidBody(groundRigidBody);
btDefaultMotionState* fallMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,50,0)));
btScalar mass = 1;
btVector3 fallInertia(0,0,0);
fallShape->calculateLocalInertia(mass,fallInertia);
btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(mass,fallMotionState,fallShape,fallInertia);
btRigidBody* fallRigidBody = new btRigidBody(fallRigidBodyCI);
dynamicsWorld->addRigidBody(fallRigidBody);
for (int i=0 ; i<300 ; i++)
{
dynamicsWorld->stepSimulation(1/60.f,10);
btTransform trans;
fallRigidBody->getMotionState()->getWorldTransform(trans);
std::cout << "sphere height: " << trans.getOrigin().getX()<<" "
<<trans.getOrigin().getY()<<" "<<trans.getOrigin().getZ() << std::endl;
}
dynamicsWorld->removeRigidBody(fallRigidBody);
delete fallRigidBody->getMotionState();
delete fallRigidBody;
dynamicsWorld->removeRigidBody(groundRigidBody);
delete groundRigidBody->getMotionState();
delete groundRigidBody;
delete fallShape;
delete groundShape;
delete dynamicsWorld;
delete solver;
delete dispatcher;
delete collisionConfiguration;
delete broadphase;
return 0;
}
上面的源代码是依照Bullet的示例代码改写的,力争代码数量最少。示例实现是的对一个刚体小球从50单位的地方自由落体下落到地面上的整个过程。代码只是输出了每个是个小球的高度而已,在dos界面输出。
为了更直观一点,将物理模拟的高度通过OSG中小球的来显示出来,给人一种直观的感受:
#include <iostream>
#include <btBulletDynamicsCommon.h>
//------------------- 在OSG中显示-begin- -----------------------
#include <osgViewer/Viewer>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include <osg/ShapeDrawable>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/Optimizer>
#include <osglib.h>
//------------------- 在OSG中显示-end- -----------------------
int main (void)
{
btBroadphaseInterface* broadphase = new btDbvtBroadphase();
btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration();
btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);
btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
dynamicsWorld->setGravity(btVector3(0,-10,0));
btCollisionShape* groundShape = new btStaticPlaneShape(btVector3(0,1,0),1);
btCollisionShape* fallShape = new btSphereShape(1);
btDefaultMotionState* groundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,-1,0)));
btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI(0,groundMotionState,groundShape,btVector3(0,0,0));
btRigidBody* groundRigidBody = new btRigidBody(groundRigidBodyCI);
dynamicsWorld->addRigidBody(groundRigidBody);
btDefaultMotionState* fallMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,50,0)));
btScalar mass = 1;
btVector3 fallInertia(0,0,0);
fallShape->calculateLocalInertia(mass,fallInertia);
btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(mass,fallMotionState,fallShape,fallInertia);
btRigidBody* fallRigidBody = new btRigidBody(fallRigidBodyCI);
dynamicsWorld->addRigidBody(fallRigidBody);
/*for (int i=0 ; i<300 ; i++)
{
dynamicsWorld->stepSimulation(1/60.f,10);
btTransform trans;
fallRigidBody->getMotionState()->getWorldTransform(trans);
std::cout << "sphere height: " << trans.getOrigin().getX()<<" "
<<trans.getOrigin().getY()<<" "<<trans.getOrigin().getZ() << std::endl;
} */
//------------------- 在OSG中显示-begin- -----------------------
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
osg::ref_ptr<osg::Group> root = new osg::Group;
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
osg::ref_ptr<osg::TessellationHints> hints = new osg::TessellationHints;
hints->setDetailRatio(0.5f);
for (int i=0 ; i<30 ; i++)
{
dynamicsWorld->stepSimulation(1/60.f,10);
btTransform trans;
fallRigidBody->getMotionState()->getWorldTransform(trans);
std::cout << "sphere height: " << trans.getOrigin().getX()<<" "
<<trans.getOrigin().getY()<<" "<<trans.getOrigin().getZ() << std::endl;
geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(trans.getOrigin().getX(),
trans.getOrigin().getZ(),
trans.getOrigin().getY()),0.01f),hints.get()));//bullet的坐标系与OpenGL相同,但是OSG与OpenGL不同,故有调整
}
root->addChild(geode.get());
osgUtil::Optimizer optimizer;
optimizer.optimize(root.get());
viewer->setSceneData(root.get());
viewer->realize();
viewer->run();
//------------------- 在OSG中显示-end- -----------------------
dynamicsWorld->removeRigidBody(fallRigidBody);
delete fallRigidBody->getMotionState();
delete fallRigidBody;
dynamicsWorld->removeRigidBody(groundRigidBody);
delete groundRigidBody->getMotionState();
delete groundRigidBody;
delete fallShape;
delete groundShape;
delete dynamicsWorld;
delete solver;
delete dispatcher;
delete collisionConfiguration;
delete broadphase;
return 0;
}
相信大家看完Bullet的第一个示例代码后会有一个很直观的感受,就是在原来自己所学的渲染中可以加入这种客观的物理性的东西,那么你的作品将会更加真实有趣。下面就附上Bullet的官方源代码,供大家学习钻研:
#include "btBulletDynamicsCommon.h"
#include <stdio.h>
int main(int argc, char** argv)
{
int i;
///-----initialization_start-----
///collision configuration contains default setup for memory, collision setup. Advanced users can create their own configuration.
//碰撞配置包括默认的内存设置、碰撞设置。高级用户可以创建自己的配置。
btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration();
///use the default collision dispatcher. For parallel processing you can use a diffent dispatcher (see Extras/BulletMultiThreaded)
//使用默认的碰撞调度器。对于并行处理,你可以用不同的调度器。
btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);
///btDbvtBroadphase is a good general purpose broadphase. You can also try out btAxis3Sweep.
//btDbvtBroadphase是很好的通用broadphase。
btBroadphaseInterface* overlappingPairCache = new btDbvtBroadphase();
///the default constraint solver. For parallel processing you can use a different solver (see Extras/BulletMultiThreaded)
//默认的约束解答器(物理模拟器)。
btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,overlappingPairCache,solver,collisionConfiguration);
dynamicsWorld->setGravity(btVector3(0,-10,0));
///-----initialization_end-----
///create a few basic rigid bodies
//创建一些基本的刚体
btCollisionShape* groundShape = new btBoxShape(btVector3(btScalar(50.),btScalar(50.),btScalar(50.)));
//keep track of the shapes, we release memory at exit.
//make sure to re-use collision shapes among rigid bodies whenever possible!
//记录这些形状,在退出时释放内存。并尽可能确保重用碰撞刚体之间的形状。
btAlignedObjectArray<btCollisionShape*> collisionShapes;
collisionShapes.push_back(groundShape);
btTransform groundTransform;
groundTransform.setIdentity();
groundTransform.setOrigin(btVector3(0,-56,0));
{
btScalar mass(0.);
//rigid body is dynamic if and only if mass is non zero, otherwise static
//刚体是动态的,当且仅当质量不为0时,否则都是静态的。
bool isDynamic = (mass != 0.f);
btVector3 localInertia(0,0,0);
if (isDynamic)
groundShape->calculateLocalInertia(mass,localInertia);
//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
//推荐使用motionstate,它提供插值功能,并且同步激活对象。
btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform);
btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,groundShape,localInertia);
btRigidBody* body = new btRigidBody(rbInfo);
//add the body to the dynamics world
//添加刚体到动态的世界中。
dynamicsWorld->addRigidBody(body);
}
{
//create a dynamic rigid body
//btCollisionShape* colShape = new btBoxShape(btVector3(1,1,1));
btCollisionShape* colShape = new btSphereShape(btScalar(1.));
collisionShapes.push_back(colShape);
/// Create Dynamic Objects
btTransform startTransform;
startTransform.setIdentity();
btScalar mass(1.f);
//rigid body is dynamic if and only if mass is non zero, otherwise static
bool isDynamic = (mass != 0.f);
btVector3 localInertia(0,0,0);
if (isDynamic)
colShape->calculateLocalInertia(mass,localInertia);
startTransform.setOrigin(btVector3(2,10,0));
//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);
btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,colShape,localInertia);
btRigidBody* body = new btRigidBody(rbInfo);
dynamicsWorld->addRigidBody(body);
}
/// Do some simulation
///-----stepsimulation_start-----
for (i=0;i<100;i++)
{
dynamicsWorld->stepSimulation(1.f/60.f,10);
//print positions of all objects
for (int j=dynamicsWorld->getNumCollisionObjects()-1; j>=0 ;j--)
{
btCollisionObject* obj = dynamicsWorld->getCollisionObjectArray()[j];
btRigidBody* body = btRigidBody::upcast(obj);
if (body && body->getMotionState())
{
btTransform trans;
body->getMotionState()->getWorldTransform(trans);
printf("world pos = %f,%f,%f\n",float(trans.getOrigin().getX()),float(trans.getOrigin().getY()),float(trans.getOrigin().getZ()));
}
}
}
///-----stepsimulation_end-----
//cleanup in the reverse order of creation/initialization
//逆向清除创建或者初始化
///-----cleanup_start-----
//remove the rigid bodies from the dynamics world and delete them
for (i=dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--)
{
btCollisionObject* obj = dynamicsWorld->getCollisionObjectArray()[i];
btRigidBody* body = btRigidBody::upcast(obj);
if (body && body->getMotionState())
{
delete body->getMotionState();
}
dynamicsWorld->removeCollisionObject( obj );
delete obj;
}
//delete collision shapes
for (int j=0;j<collisionShapes.size();j++)
{
btCollisionShape* shape = collisionShapes[j];
collisionShapes[j] = 0;
delete shape;
}
//delete dynamics world
delete dynamicsWorld;
//delete solver
delete solver;
//delete broadphase
delete overlappingPairCache;
//delete dispatcher
delete dispatcher;
delete collisionConfiguration;
//next line is optional: it will be cleared by the destructor when the array goes out of scope
collisionShapes.clear();
///-----cleanup_end-----
}
Bullet后续的学习心得将继续奉献给大家。