(C++) Simple Bullet Physics Class/Manager
Posted: Fri Jul 02, 2010 7:15 pm
Well, I thought I might finally contribute to the Irrlicht community by sharing some of my code
Below is a class that I have created for my own project, and that I have been using for Bullet physics integration. Its by no means a wrapper, as it only gets the basic necessities for physics up and running. I myself use it in other functions to control my physics world.
In its original state, it required pointers to some other classes that are specific to my project, so I did a bit of editing to remove that requirement. I have however not tested the edited version, but I believe it should work fine. If there are any issues, I'll be happy to fix them
Now, to give credit where it is due -> I borrowed code from several sources to create this, although I have forgotten who most of them are
Some of it is from various Bullet-Irrlicht tutorials, other pieces are either taken from, or inspired by irrBullet.
Its meant for those that want to quickly implement Bullet into their projects, but don't want a full wrapper.
So, first off, some screenies ->
A standard box stack

The same stack after an explosion ->

And, the resulting rubble after an "attraction"
->

So, here is CPhysics.h --
And CPhysics.cpp--
So, to use this class, add
Somewhere before your main loop. Here, MapNode is a pointer to your Level geometry scene node (in this case, its an AnimatedMeshSceneNode, but you can change that to whatever type your Level is)
After that, simply call
At the start of your Game Loop.
If you also want the debug drawer, then include DebugDraw.h -
Add the following after creating the physics world -
Add this before your Game Loop -
And, finally, add this before your call to driver->endScene() -
For a simple test, here is the code to create the stack of boxes as shown in the screenie -
Any comments/critiques are welcome
Enjoy!

In its original state, it required pointers to some other classes that are specific to my project, so I did a bit of editing to remove that requirement. I have however not tested the edited version, but I believe it should work fine. If there are any issues, I'll be happy to fix them

Now, to give credit where it is due -> I borrowed code from several sources to create this, although I have forgotten who most of them are

Its meant for those that want to quickly implement Bullet into their projects, but don't want a full wrapper.
So, first off, some screenies ->
A standard box stack


The same stack after an explosion ->

And, the resulting rubble after an "attraction"


So, here is CPhysics.h --
Code: Select all
#ifndef _CPHYSICS_H
#define _CPHYSICS_H
#include <btBulletCollisionCommon.h>
#include <btBulletDynamicsCommon.h>
#include <BulletCollision\CollisionDispatch\btGhostObject.h>
class CPhysics
{
public:
CPhysics(ISceneManager* smgr, IVideoDriver* driver, ITimer* Timer, btVector3 Gravity, IAnimatedMeshSceneNode* Map);
void Update();
bool UpdateRender(btRigidBody* TObject);
btTriangleMesh* ConvertIrrMeshToBulletTriangleMesh(IMesh* pMesh,const vector3df& scaling);
void QuaternionToEuler(const btQuaternion &TQuat, btVector3 &TEuler);
void getConvexHull(IMesh *collMesh, btConvexHullShape *hullShape, IAnimatedMeshSceneNode* node);
list<btRigidBody *> getObjectList();
btRigidBody* CreateBox(const btVector3 &TPosition, const vector3df &TScale, btScalar TMass);
btRigidBody* loadConvex(std::string filename, const btVector3 &TPosition, const vector3df &TScale, btScalar TMass);
btDiscreteDynamicsWorld* getWorld();
private:
ISceneManager* smgr;
IVideoDriver* driver;
ITimer* Timer;
btDiscreteDynamicsWorld* World;
list<btRigidBody *> Objects;
btVector3 Gravity;
u32 TimeStamp;
u32 DeltaTime;
};
#endif
Code: Select all
#include "CPhysics.h"
CPhysics::CPhysics(ISceneManager* smgr, IVideoDriver* driver, ITimer* Timer, btVector3 Gravity, IAnimatedMeshSceneNode* Map)
{
btDefaultCollisionConfiguration* CollisionConfiguration = new btDefaultCollisionConfiguration();
btBroadphaseInterface* BroadPhase = new btAxisSweep3(btVector3(-100000, -100000, -100000), btVector3(100000, 100000, 100000));
BroadPhase->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback());
btCollisionDispatcher* Dispatcher = new btCollisionDispatcher(CollisionConfiguration);
btSequentialImpulseConstraintSolver* Solver = new btSequentialImpulseConstraintSolver();
CPhysics::World = new btDiscreteDynamicsWorld(Dispatcher, BroadPhase, Solver, CollisionConfiguration);
CPhysics::World->setGravity(Gravity);
CPhysics::Gravity = Gravity;
CPhysics::smgr = smgr;
CPhysics::driver = driver;
CPhysics::Timer = Timer;
CPhysics::TimeStamp = Timer->getTime();
CPhysics::DeltaTime = 0;
btTriangleMesh* indexVertexArrays = CPhysics::ConvertIrrMeshToBulletTriangleMesh(Map->getMesh(),Map->getMesh()->getScale());
btBvhTriangleMeshShape* trimeshShape = new btBvhTriangleMeshShape(indexVertexArrays, true);
btQuaternion quat;
quat.setEulerZYX(0,0,0);
btTransform Transform2;
Transform2.setIdentity();
Transform2.setOrigin(btVector3(0,0,0));
Transform2.setRotation(quat);
btDefaultMotionState *MotionState2 = new btDefaultMotionState(Transform2);
btRigidBody *RigidBody = new btRigidBody(0, MotionState2, trimeshShape);
RigidBody->setUserPointer((void *)(Map->getNode()));
CPhysics::World->addRigidBody(RigidBody);
CPhysics::Objects.push_back(RigidBody);
RigidBody->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
}
void CPhysics::Update()
{
CPhysics::DeltaTime = CPhysics::Timer->getTime() - CPhysics::TimeStamp;
CPhysics::TimeStamp = CPhysics::Timer->getTime();
CPhysics::World->stepSimulation(CPhysics::DeltaTime * 0.001f, 2);
for(list<btRigidBody *>::Iterator Iterator = CPhysics::Objects.begin(); Iterator != CPhysics::Objects.end();)
if(!CPhysics::UpdateRender(*Iterator))
{
Iterator = CPhysics::Objects.erase(Iterator);
}
else
{
Iterator++;
}
}
bool CPhysics::UpdateRender(btRigidBody* TObject)
{
ISceneNode* Node = static_cast<ISceneNode *>(TObject->getUserPointer());
if(Node == NULL)
{
return false;
}
const btVector3& Point = TObject->getCenterOfMassPosition();
Node->setPosition(vector3df((f32)Point[0], (f32)Point[1], (f32)Point[2]));
btVector3 EulerRotation;
CPhysics::QuaternionToEuler(TObject->getOrientation(), EulerRotation);
Node->setRotation(vector3df(EulerRotation[0], EulerRotation[1], EulerRotation[2]));
return true;
}
btRigidBody* CPhysics::CreateBox(const btVector3 &TPosition, const vector3df &TScale, btScalar TMass)
{
ISceneNode* Node = CPhysics::smgr->addCubeSceneNode(1.0f);
Node->setScale(TScale);
Node->setMaterialFlag(EMF_LIGHTING, true);
Node->setMaterialFlag(EMF_NORMALIZE_NORMALS, true);
//Node->setMaterialTexture(0, driver->getTexture("rust0.jpg"));
btTransform Transform;
Transform.setIdentity();
Transform.setOrigin(TPosition);
btDefaultMotionState *MotionState = new btDefaultMotionState(Transform);
btVector3 HalfExtents(TScale.X * 0.5f, TScale.Y * 0.5f, TScale.Z * 0.5f);
btCollisionShape *Shape = new btBoxShape(HalfExtents);
btVector3 LocalInertia;
Shape->calculateLocalInertia(TMass, LocalInertia);
btRigidBody *RigidBody = new btRigidBody(TMass, MotionState, Shape, LocalInertia);
RigidBody->setUserPointer((void *)(Node));
RigidBody->setActivationState(DISABLE_DEACTIVATION);
CPhysics::World->addRigidBody(RigidBody);
CPhysics::Objects.push_back(RigidBody);
return RigidBody;
}
btTriangleMesh* CPhysics::ConvertIrrMeshToBulletTriangleMesh(IMesh* pMesh,const vector3df& scaling)
{
btVector3 vertices[3];
u32 i,j,k,index,numVertices,numIndices;
u16* mb_indices;
btTriangleMesh *pTriMesh = new btTriangleMesh();
for (i=0; i<pMesh->getMeshBufferCount(); i++)
{
IMeshBuffer* mb=pMesh->getMeshBuffer(i);
if(mb->getVertexType()==EVT_STANDARD)
{
S3DVertex* mb_vertices=(S3DVertex*)mb->getVertices();
mb_indices = mb->getIndices();
numVertices = mb->getVertexCount();
numIndices = mb->getIndexCount();
for(j=0;j<numIndices;j+=3)
{
for (k=0;k<3;k++)
{
index = mb_indices[j+k];
vertices[k] = btVector3(mb_vertices[index].Pos.X*scaling.X, mb_vertices[index].Pos.Y*scaling.Y, mb_vertices[index].Pos.Z*scaling.Z);
}
pTriMesh->addTriangle(vertices[0], vertices[1], vertices[2]);
}
}
else if(mb->getVertexType()==EVT_2TCOORDS)
{
S3DVertex2TCoords* mb_vertices=(S3DVertex2TCoords*)mb->getVertices();
mb_indices = mb->getIndices();
numVertices = mb->getVertexCount();
numIndices = mb->getIndexCount();
for(j=0;j<numIndices;j+=3)
{
for (k=0;k<3;k++)
{
index = mb_indices[j+k];
vertices[k] = btVector3(mb_vertices[index].Pos.X*scaling.X, mb_vertices[index].Pos.Y*scaling.Y, mb_vertices[index].Pos.Z*scaling.Z);
}
pTriMesh->addTriangle(vertices[0], vertices[1], vertices[2]);
}
}
}
return pTriMesh;
};
void CPhysics::QuaternionToEuler(const btQuaternion &TQuat, btVector3 &TEuler)
{
btScalar W = TQuat.getW();
btScalar X = TQuat.getX();
btScalar Y = TQuat.getY();
btScalar Z = TQuat.getZ();
float WSquared = W * W;
float XSquared = X * X;
float YSquared = Y * Y;
float ZSquared = Z * Z;
TEuler.setX(atan2f(2.0f * (Y * Z + X * W), -XSquared - YSquared + ZSquared + WSquared));
TEuler.setY(asinf(-2.0f * (X * Z - Y * W)));
TEuler.setZ(atan2f(2.0f * (X * Y + Z * W), XSquared - YSquared - ZSquared + WSquared));
TEuler *= RADTODEG;
};
btRigidBody* CPhysics::loadConvex(std::string filename, const btVector3 &TPosition, const vector3df &TScale, btScalar TMass)
{
IAnimatedMeshSceneNode* Node = CPhysics::smgr->addAnimatedMeshSceneNode((IAnimatedMesh*)CPhysics::smgr->getMesh(filename.c_str()));
btTriangleMesh* trimesh = CPhysics::ConvertIrrMeshToBulletTriangleMesh(Node->getMesh(), Node->getScale());
btConvexShape* hull = new btConvexTriangleMeshShape(trimesh);
hull->setUserPointer(hull);
btVector3 localInertia(0,0,0);
hull->calculateLocalInertia(TMass, localInertia);
btQuaternion quat;
quat.setEulerZYX(Node->getRotation().X,Node->getRotation().Y,Node->getRotation().Z);
btTransform Transform2;
Transform2.setIdentity();
Transform2.setOrigin(TPosition);
Transform2.setRotation(quat);
btDefaultMotionState *MotionState2 = new btDefaultMotionState(Transform2);
btRigidBody* RigidBody = new btRigidBody(TMass, MotionState2, hull, localInertia);
RigidBody->setUserPointer((void *)(Node));
RigidBody->setActivationState(DISABLE_DEACTIVATION);
CPhysics::World->addRigidBody(RigidBody);
CPhysics::Objects.push_back(RigidBody);
return RigidBody;
}
list<btRigidBody *> CPhysics::getObjectList()
{
return CPhysics::Objects;
}
btDiscreteDynamicsWorld* CPhysics::getWorld()
{
return CPhysics::World;
}
Code: Select all
CPhysics* Physics = new CPhysics(smgr, driver, Timer, btVector3(0, -500, 0), MapNode);
After that, simply call
Code: Select all
Physics->Update();
If you also want the debug drawer, then include DebugDraw.h -
Code: Select all
#ifndef _DEBUGDRAW_H
#define _DEBUGDRAW_H
enum DebugDrawModes
{
DBG_NoDebug=0,
DBG_DrawWireframe = 1,
DBG_DrawAabb=2,
DBG_DrawFeaturesText=4,
DBG_DrawContactPoints=8,
DBG_NoDeactivation=16,
DBG_NoHelpText = 32,
DBG_DrawText=64,
DBG_ProfileTimings = 128,
DBG_EnableSatComparison = 256,
DBG_DisableBulletLCP = 512,
DBG_EnableCCD = 1024,
DBG_MAX_DEBUG_DRAW_MODE
};
//Courtesy of randomMESH
class DebugDraw : public btIDebugDraw
{
public:
DebugDraw(IrrlichtDevice* const device) :
mode(DBG_NoDebug), driver(device->getVideoDriver()), logger(device->getLogger())
{
}
void drawLine(const btVector3& from, const btVector3& to, const btVector3& color)
{
SColor newColor(255, (u32)color[0], (u32)color[1], (u32)color[2]);
if (color[0] <= 1.0 && color[0] > 0.0)
newColor.setRed((u32)(color[0]*255.0));
if (color[1] <= 1.0 && color[1] > 0.0)
newColor.setGreen((u32)(color[1]*255.0));
if (color[2] <= 1.0 && color[2] > 0.0)
newColor.setBlue((u32)(color[2]*255.0));
this->driver->draw3DLine(vector3df(from[0], from[1], from[2]), vector3df(to[0], to[1], to[2]), newColor);
}
void drawContactPoint(const btVector3& PointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color)
{
static const SColor CONTACTPOINT_COLOR(255, 255, 255, 0);
const btVector3 to(PointOnB + normalOnB*distance);
this->driver->draw3DLine(vector3df(PointOnB[0], PointOnB[1], PointOnB[2]),vector3df(to[0], to[1], to[2]),CONTACTPOINT_COLOR);
}
void reportErrorWarning(const char* text)
{
this->logger->log(text, irr::ELL_ERROR);
}
void draw3dText(const btVector3& location, const char* text) { }
void setDebugMode(int mode) { this->mode = mode; }
int getDebugMode() const { return this->mode; }
private:
int mode;
IVideoDriver* const driver;
ILogger* logger;
};
#endif
Code: Select all
DebugDraw* Debug = new DebugDraw(device);
Physics->getWorld()->setDebugDrawer(Debug);
Debug->setDebugMode(DBG_DrawAabb | DBG_DrawWireframe);
Code: Select all
SMaterial debugMat;
debugMat.Lighting = false;
Code: Select all
driver->setMaterial(debugMat);
driver->setTransform(ETS_WORLD, IdentityMatrix);
Physics->getWorld()->debugDrawWorld();
Code: Select all
int xshift = 0;
int yshift = 0;
for(yshift=0;yshift<=7;yshift++)
for(xshift=0;xshift<=7;xshift++)
{
btRigidBody* DummyRigidBody = Physics->CreateBox(btVector3((100+(50*xshift)),10+(50*yshift),1500),vector3df(50,50,50),40);
};

Enjoy!