一、bullet引擎简介
Bullet是一个开源的物理模拟计算引擎,世界三大物理引擎模拟之一(另外两种是Havok和PhysX)。广泛应用于游戏开发和电影制作之中。Bullet也是AMD开放物理计划成员之一。
Bullet是一个跨平台的物理模拟计算引擎。支持Windows、Linux、MAC等多个平台。
二、bullet的主要函数
在bullet中要实现物理世界需要三个函数,initPhysics()、stepSimulation()、exitPhysics()。
1、 initPhysics()函数
initPhysics()函数是初始化物理世界的一个函数。在该函数中首先要创建一个动态世界:btDiscreteDynamicsWorld* dynamicsWorld; 要想创建成功一个动态世界还需要四个参数,
分别为:
btBroadphaseInterface* broadphase;
btDefaultCollisionConfiguration*collisionConfiguration;
btCollisionDispatcher* dispatcher;
btSequentialImpulseConstraintSolver* solver;
这几个参数分别表示:
broadphase:一个阶段算法,目的是尽量剔除没有相互作用的对象对
collisionConfiguration:碰撞配置
dispatcher:碰撞调度器对象
solver:创建解算器,用于求解约束方程。
这四个类只需要实例化当做参数传入创建一个动态物理世界,这就成功的创建了一个空的动态物理世界,
然后再在空的物理世界中加入其它的属性,首先加入一个重力,通过setGravity()设置物理世界的重力。接下来创建两个刚体btRigidBody* fallRigidBody; btRigidBody* groundRigidBody; 第一个刚体作为物理世界中的地面,第二个作为加入物理世界中的物体。创建一个刚要需要给刚体四个属性:mass(质量)、btDefaultMotionState(刚体的位置信息)、btCollisionShape(刚体形状)、fallInertia(惯性)。创建成功后,addRigidBody()加入到物理世界中。
如此,初始化就完成了。
2、stepSimulation()
该函数主要是负责物理世界的更新,在每个时间段更新物理世界。这是Bullet中基类的一个虚函数,可以根据用户自己的需要进行重写调用。如果只是简单的加入一个物理世界的话,直接在framestarted函数中调用即可,但是要给它一个时间参数,让它知道什么时候更新。
3、exitPhysics()函数
该函数负责退出物理世界以及销毁物理世界。
该函数主要是在析构函数中调用。
三、Ogre中加入bullet物理引擎。
第一步、引用,引用该头文件,创建一个简单的物理世界所需的所有类和接口基本都在这里面。我在Ogre中提供的一个demo中加入了物理世界。首先将以及写好的initPhysics()函数放入setupContext()(建立场景的函数)中调用,(注:在这里调用该函数只是相当于创建了一个变量而没有使用它。)
第二步、接下来在ogre中的frameStarted()函数中调用stepSimulation()给它的参数为每一帧渲染完之后的时间(evt.timeSinceLastFrame 这是Ogre中已经封装好的直接用就可以)。
第三步、最后调用exitPhysics()函数销毁该物理世界即可。
第四步、物理世界只是相当于一种算法,一种规则,是抽象的,我们要想在demo中看到自己所创建的物理世界以及世界中我们创建的地面、物体刚体等需要用到bullet中提供的debugdraw的一个类,该类是bullet封装好让用户可以用来调试物理引擎的一个类。
成功加入物理世界后的demo截图:
四、具体实现代码
定义的几个类,作为参数传入建立动态物理世界
btBroadphaseInterface* broadphase;
btDefaultCollisionConfiguration* collisionConfiguration;
btCollisionDispatcher* dispatcher;
btSequentialImpulseConstraintSolver* solver;
btDiscreteDynamicsWorld* dynamicsWorld;
btCollisionShape* groundShape;
btCollisionShape* fallShape;
btRigidBody* fallRigidBody;
btRigidBody* groundRigidBody;
btRigidBody* fallRigidBody1;
brPhysicsDebugDrawer* m_pPhysicDebugDraw;
initPhysics()函数
void initPhysics()
{
// helper->setUpAxis(1);
// helper->createPhysicsDebugDrawer(dynamicsWorld);
// if (dynamicsWorld->getDebugDrawer())
// dynamicsWorld->getDebugDrawer()->setDebugMode(btIDebugDraw::DBG_DrawWireframe+btIDebugDraw::DBG_DrawContactPoints);
//创建broadphase阶段算法,目的是尽量的剔除没有相互作用的对象对
broadphase = new btDbvtBroadphase();
//创建碰撞配置对象以及碰撞调度器对象,使我们可以再各个阶段尝试不同的算法组合,目的是使用不同的算法和测试相同的碰撞
collisionConfiguration = new btDefaultCollisionConfiguration();
dispatcher = new btCollisionDispatcher(collisionConfiguration);
//创建解算器,用于求解约束方程。得到物体在重力等作用下的最终位置的
solver = new btSequentialImpulseConstraintSolver;
//创建一个动态世界
dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
//设置动力学世界的重力为Y轴向下方向
dynamicsWorld->setGravity(btVector3(0,-10,0));
//创建平面和一个球体,第一个参数是平面的法向,第二个参数是在法线方向的偏移,注意,动力学世界方向是Y轴下方向
groundShape = new btStaticPlaneShape(btVector3(0,1,0),1);
//1为球体的半径
fallShape = new btSphereShape(10);
groundShape = new btStaticPlaneShape(btVector3(0,1,0),1);
//1为球体的半径
fallShape = new btSphereShape(10);
// if (dynamicsWorld->getDebugDrawer())
// dynamicsWorld->getDebugDrawer()->setDebugMode(btIDebugDraw::DBG_DrawWireframe+btIDebugDraw::DBG_DrawContactPoints);
//加入一个平面到动力学世界
btDefaultMotionState* groundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,-1,0)));
btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI(0,groundMotionState,groundShape,btVector3(0,0,0));
groundRigidBody = new btRigidBody(groundRigidBodyCI);//绑定操作
dynamicsWorld->addRigidBody(groundRigidBody);
//加入一个球体
btDefaultMotionState* fallMotionState =
new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,0,0)));
btScalar mass = 1; //质量
btVector3 fallInertia(0,0,0);//惯性
fallShape->calculateLocalInertia(mass,fallInertia); //通过质量,这个函数计算出运动物体的惯性
btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(mass,fallMotionState,fallShape,fallInertia);
fallRigidBody = new btRigidBody(fallRigidBodyCI);
// fallMotionState->setWorldTransform(btVector3(0, -10, 0));
dynamicsWorld->addRigidBody(fallRigidBody);
btVector3 v=fallRigidBody->getWorldTransform().getOrigin();
std::cout<<"3213213213213211111"<<fallRigidBody->getWorldTransform().getOrigin().getX()<<std::endl;
std::cout<<"ABCDE"<<fallRigidBody->getWorldTransform().getOrigin().getY()<<std::endl;
std::cout<<"BDAZCZ"<<fallRigidBody->getWorldTransform().getOrigin().getZ()<<std::endl;
// helper->autogenerateGraphicsObjects(dynamicsWorld);
btDefaultMotionState* fallMotionState2 =
new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,0,0)));
btScalar mass1 = 1; //质量
btVector3 fallInertia1(0,0,0);//惯性
fallShape->calculateLocalInertia(mass1,fallInertia1); //通过质量,这个函数计算出运动物体的惯性
btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI1(mass1,fallMotionState2,fallShape,fallInertia1);
fallRigidBody1 = new btRigidBody(fallRigidBodyCI1);
// fallMotionState->setWorldTransform(btVector3(0, -10, 0));
dynamicsWorld->addRigidBody(fallRigidBody1);
std::cout<<"3213213213213211111"<<fallRigidBody1->getWorldTransform().getOrigin().getX()<<std::endl;
std::cout<<"ABCDE"<<fallRigidBody1->getWorldTransform().getOrigin().getY()<<std::endl;
std::cout<<"BDAZCZ"<<fallRigidBody1->getWorldTransform().getOrigin().getZ()<<std::endl;
}
initPhysics()函数调用位置:
void setupBody(SceneManager* sceneMgr)
{
initPhysics();
//create main model
mBodyNode = sceneMgr->getRootSceneNode()->createChildSceneNode(Vector3::UNIT_Y * CHAR_HEIGHT);
mBodyEnt = sceneMgr->createEntity("SinbadBody", "Sinbad.mesh");
mBodyNode->attachObject(mBodyEnt);
//create swords and attach to sheath
LogManager::getSingleton().logMessage("Creatingswords");
mSword1 = sceneMgr->createEntity("SinbadSword1", "Sword.mesh");
mSword2 = sceneMgr->createEntity("SinbadSword2", "Sword.mesh");
mBodyEnt->attachObjectToBone("Sheath.L", mSword1);
mBodyEnt->attachObjectToBone("Sheath.R", mSword2);
nBodyNode = sceneMgr->getRootSceneNode()->createChildSceneNode(Vector3::UNIT_Y * CHAR_HEIGHT);
std::cout<<"8888888"<<Vector3::UNIT_Y<<std::endl;
mBodyEnt1 = sceneMgr->createEntity("SinbadBody1", "Sinbad.mesh");
nBodyNode->attachObject(mBodyEnt1);
//create swords and attach to sheath
LogManager::getSingleton().logMessage("Creatingswords11");
mSword11 = sceneMgr->createEntity("s1", "Sword.mesh");
mSword22 = sceneMgr->createEntity("SinbadSword22", "Sword.mesh");
mBodyEnt1->attachObjectToBone("Sheath.L", mSword11);
mBodyEnt1->attachObjectToBone("Sheath.R", mSword22);
LogManager::getSingleton().logMessage("Creatingthe chains");
// create a couple of ribbon trails for theswords, just for fun
NameValuePairList params;
params["numberOfChains"] = "2";
params["maxElements"] = "80";
mSwordTrail = (RibbonTrail*)sceneMgr->createMovableObject("RibbonTrail", ¶ms);
mSwordTrail->setMaterialName("Examples/LightRibbonTrail");
mSwordTrail->setTrailLength(20);
mSwordTrail->setVisible(false);
sceneMgr->getRootSceneNode()->attachObject(mSwordTrail);
for (int i = 0; i < 2; i++)
{
mSwordTrail->setInitialColour(i, 1, 0.8, 0);
mSwordTrail->setColourChange(i, 0.75, 1.25, 1.25, 1.25);
mSwordTrail->setWidthChange(i, 1);
mSwordTrail->setInitialWidth(i, 0.5);
}
mKeyDirection = Vector3::ZERO;
nKeyDirection =Vector3::ZERO;
mVerticalVelocity = 0;
nVerticalVelocity = 0;
}
stepSimulation()函数调用位置:
void addTime(Real deltaTime)
{
updateBody(deltaTime);
updateAnimations(deltaTime);
updateBodytwo(deltaTime);
updateAnimationstwo(deltaTime);
updateCamera(deltaTime);
dynamicsWorld->stepSimulation(deltaTime);
// physicsDebugDraw(1);
}
bool frameStarted(const FrameEvent&evt)
{
//let character update animations and camera
mChara->addTime(evt.timeSinceLastFrame);
mChara->frameStarted(evt);
mChara->physicsDebugDraw(1);
return SdkSample::frameStarted(evt);
}
exitPhysics()函数:
void exitPhysics()
{
delete dynamicsWorld;
delete solver;
delete dispatcher;
delete collisionConfiguration;
delete broadphase;
}
调用exitPhysics()函数:
void cleanupContent()
{
//clean up character controller and the floor mesh
if (mChara)
{
mChara->exitPhysics();
delete mChara;
mChara = 0;
}
MeshManager::getSingleton().remove("floor", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
}
调试物理世界函数:
DebugDraw.h
#ifndef DebugDrawer_h__
#define DebugDrawer_h__
//#include "../common.h"
#include "Ogre.h"_
#include "btBulletDynamicsCommon.h"
//#include "btShapeHull.h"
/** Ogre的三维向量转为bt的三维向量
* @param V Ogre的三维向量
* @return bt的三维向量
*/
inline btVector3 cvt(const Ogre::Vector3 &V){
return btVector3(V.x, V.y, V.z);
}
/** bt的三维向量转为Ogre的三维向量
* @param V bt的三维向量
* @return Ogre的三维向量
*/
inline Ogre::Vector3 cvt(const btVector3&V){
return Ogre::Vector3(V.x(), V.y(), V.z());
}
/** Ogre的四元数转为bt的四元数
* @param Q Ogre的四元数
* @return bt的四元数
*/
inline btQuaternion cvt(const Ogre::Quaternion &Q)
{
return btQuaternion(Q.x, Q.y, Q.z, Q.w);
};
/** bt的四元数转为Ogre的四元数
* @param Q bt的四元数
* @return Ogre的四元数
*/
inline Ogre::Quaternion cvt(const btQuaternion &Q)
{
return Ogre::Quaternion(Q.w(), Q.x(), Q.y(), Q.z());
};
class brPhysicsDebugDrawer: public btIDebugDraw,public Ogre::FrameListener
{
public:
brPhysicsDebugDrawer( Ogre::SceneManager *scm);
~brPhysicsDebugDrawer();
virtual void drawLine (const btVector3 &from, const btVector3 &to, const btVector3 &color);
virtual void drawTriangle (const btVector3 &v0, const btVector3 &v1, const btVector3 &v2, const btVector3 &color, btScalar);
virtual void drawContactPoint (const btVector3 &PointOnB, const btVector3 &normalOnB, btScalar distance,int lifeTime, const btVector3 &color);
virtual void reportErrorWarning (const char *warningString);
virtual void draw3dText (const btVector3 &location, const char *textString);
virtual void setDebugMode (int debugMode);
virtual int getDebugMode () const;
protected:
bool frameStarted(constOgre::FrameEvent& evt);
bool frameEnded(constOgre::FrameEvent& evt);
private:
struct ContactPoint{
Ogre::Vector3 from;
Ogre::Vector3 to;
Ogre::ColourValue color;
size_t dieTime;
};
DebugDrawModes mDebugModes;
Ogre::ManualObject *mLines;
Ogre::ManualObject *mTriangles;
std::vector< ContactPoint >*mContactPoints;
std::vector< ContactPoint > mContactPoints1;
std::vector< ContactPoint > mContactPoints2;
};
#endif // DebugDrawer_h__
DebugDraw.cpp
#include "DebugDrawer.h"
//#include "OgreBulletUtils.h"
using namespace Ogre;
brPhysicsDebugDrawer::brPhysicsDebugDrawer( Ogre::SceneManager *scm )
{
mContactPoints =&mContactPoints1;
mLines = new ManualObject("physics lines");
assert( mLines );
mTriangles = new ManualObject("physicstriangles");
assert( mTriangles );
mLines->setDynamic(true);
mTriangles->setDynamic(true);
//mLines->estimateVertexCount(100000 );
//mLines->estimateIndexCount(0 );
Ogre::SceneNode* node =scm->getRootSceneNode()->createChildSceneNode();
node->attachObject( mLines );
node->attachObject( mTriangles);
//先隐藏
// node->setVisible(false);
static const char * matName = "OgreBulletCollisionsDebugDefault";
MaterialPtr mtl =MaterialManager::getSingleton().getDefaultSettings()->clone(matName);
mtl->setReceiveShadows(false);
mtl->setSceneBlending( SBT_TRANSPARENT_ALPHA);
mtl->setDepthBias( 0.1, 0 );
TextureUnitState * tu = mtl->getTechnique(0)->getPass(0)->createTextureUnitState();
assert( tu );
tu->setColourOperationEx( LBX_SOURCE1,LBS_DIFFUSE );
mtl->getTechnique(0)->setLightingEnabled(false);
//mtl->getTechnique(0)->setSelfIllumination(ColourValue::White );
mLines->begin( matName,RenderOperation::OT_LINE_LIST );
mLines->position( Vector3::ZERO );
mLines->colour( ColourValue::Blue );
mLines->position( Vector3::ZERO );
mLines->colour( ColourValue::Blue );
mTriangles->begin( matName,RenderOperation::OT_TRIANGLE_LIST );
mTriangles->position( Vector3::ZERO );
mTriangles->colour( ColourValue::Blue );
mTriangles->position( Vector3::ZERO );
mTriangles->colour( ColourValue::Blue );
mTriangles->position( Vector3::ZERO );
mTriangles->colour( ColourValue::Blue );
mDebugModes = (DebugDrawModes)DBG_DrawWireframe;
Root::getSingleton().addFrameListener(this);
}
brPhysicsDebugDrawer::~brPhysicsDebugDrawer()
{
Root::getSingleton().removeFrameListener(this);
delete mLines;
delete mTriangles;
}
void brPhysicsDebugDrawer::drawLine( const btVector3 &from, const btVector3 &to, const btVector3 &color )
{
ColourValue c( color.getX(), color.getY(),color.getZ() );
c.saturate();
mLines->position( cvt(from) );
mLines->colour( c );
mLines->position( cvt(to) );
mLines->colour( c );
}
void brPhysicsDebugDrawer::drawTriangle( const btVector3 &v0, const btVector3 &v1, const btVector3 &v2, const btVector3 &color, btScalar alpha )
{
ColourValue c( color.getX(), color.getY(),color.getZ(), alpha );
c.saturate();
mTriangles->position( cvt(v0) );
mTriangles->colour( c );
mTriangles->position( cvt(v1) );
mTriangles->colour( c );
mTriangles->position( cvt(v2) );
mTriangles->colour( c );
}
void brPhysicsDebugDrawer::drawContactPoint( const btVector3 &PointOnB, const btVector3 &normalOnB, btScalar distance,int lifeTime, const btVector3 &color )
{
mContactPoints->resize(mContactPoints->size() + 1 );
ContactPoint p = mContactPoints->back();
p.from = cvt( PointOnB );
p.to = p.from + cvt( normalOnB ) * distance;
p.dieTime =Root::getSingleton().getTimer()->getMilliseconds() + lifeTime;
p.color.r = color.x();
p.color.g = color.y();
p.color.b = color.z();
}
bool brPhysicsDebugDrawer::frameStarted( const Ogre::FrameEvent& evt )
{
size_t now =Root::getSingleton().getTimer()->getMilliseconds();
std::vector< ContactPoint > *newCP =mContactPoints == &mContactPoints1 ? &mContactPoints2 : &mContactPoints1;
for ( std::vector< ContactPoint >::iterator i =mContactPoints->begin(); i < mContactPoints->end(); i++ ){
ContactPoint &cp = *i;
mLines->position( cp.from );
mLines->colour( cp.color );
mLines->position( cp.to );
if ( now <= cp.dieTime )
newCP->push_back( cp );
}
mContactPoints->clear();
mContactPoints = newCP;
mLines->end();
mTriangles->end();
return true;
}
bool brPhysicsDebugDrawer::frameEnded( const Ogre::FrameEvent& evt )
{
mLines->beginUpdate(0);
mTriangles->beginUpdate(0);
return true;
}
void brPhysicsDebugDrawer::reportErrorWarning( const char *warningString )
{
LogManager::getSingleton().getDefaultLog()->logMessage( warningString);
}
void brPhysicsDebugDrawer::draw3dText( const btVector3 &location, const char *textString )
{
}
void brPhysicsDebugDrawer::setDebugMode( int debugMode )
{
mDebugModes = (DebugDrawModes) debugMode;
}
int brPhysicsDebugDrawer::getDebugMode() const
{
return mDebugModes;
}
调用调试函数
brPhysicsDebugDrawer* m_pPhysicDebugDraw;
void physicsDebugDraw(int debugFlags)
{
if (dynamicsWorld&& dynamicsWorld->getDebugDrawer())
{
dynamicsWorld->getDebugDrawer()->setDebugMode(debugFlags);
dynamicsWorld->debugDrawWorld();
}
}
void draw(SceneManager*sceneMgr)
{
m_pPhysicDebugDraw = new brPhysicsDebugDrawer(sceneMgr);
dynamicsWorld -> setDebugDrawer(m_pPhysicDebugDraw);
}
在frameStarted函数中循环调用
boolframeStarted(const FrameEvent& evt)
{
//let character update animations and camera
mChara->addTime(evt.timeSinceLastFrame);
mChara->frameStarted(evt);
mChara->physicsDebugDraw(1);
return SdkSample::frameStarted(evt);
}