Here it is...
DPhysicsManager.cpp
Code: Select all
#include "DPhysicsManager.h"
float stepSize=0.1f;
// available simulation time
float simtime=0.0f;
unsigned long lastFrame=0;
unsigned long thisFrame=0;
dWorldID DPhysicsManager::theWorld=0;
dSpaceID DPhysicsManager::theSpace=0;
dJointGroupID DPhysicsManager::theJointGroup=0;
float inline frand(int precis=4)
{
int val=(int)pow(10,precis);
return (rand()%val)/(float)val;
}
float inline max(float a, float b)
{
return a>b?a:b;
}
DPhysicsManager::DPhysicsManager(IrrlichtDevice *device)
{
std::list<DPhysicsBounceable*> bounceables;
this->device = device;
now=device->getTimer()->getTime();
this->init();
}
DPhysicsManager::~DPhysicsManager()
{}
void DPhysicsManager::updatePhysics()
{
//updateEntitiesBeforePhysics();
//we do nothing here since there are no user controlled objects in the simulation
// build the collision joints for all objects in 'theSpace'
dSpaceCollide(theSpace,0, &nearCollisionCallback);
// make a simulation step for 'theWorld'
//dWorldQuickStep(theWorld,0.05);
//dWorldStep(theWorld,0.1f);
//optionally use dWorldStepFast1 instead of dWorldStep
//when stack overflows occure or speed is important
dWorldStepFast1(theWorld,stepSize,10);
// clean up joints from collisions;
dJointGroupEmpty(theJointGroup);
// apply the new positions and rotations to the scenenodes
updateEntitiesAfterPhysics();
}
void DPhysicsManager::init()
{
bounceables.clear();
theWorld=dWorldCreate();
theSpace=dSimpleSpaceCreate(0);
theJointGroup=dJointGroupCreate(0);
// play around with these settings
dWorldSetGravity(theWorld,0,GRAVITY,0);
dWorldSetCFM (theWorld,1e-5);
dWorldSetAutoDisableFlag (theWorld,1);
dWorldSetContactMaxCorrectingVel (theWorld,0.1);
dWorldSetContactSurfaceLayer (theWorld,0.001);
}
void DPhysicsManager::addEntity(DCollisionInterface *entity, bool box, bool test, bool updatephysics)
{
DPhysicsBounceable *tempbounce = new DPhysicsBounceable(theWorld,theSpace,0,0,entity->getSceneNode(),entity->getMesh(), updatephysics);
if(!box&&!test)
{
tempbounce->setGeomData(entity->getMesh());
//tempbounce->timetolive=0;
}
if(box&&!test)
{
tempbounce->setGeomData();
}
if(test)
{
tempbounce->setGeomDataTest();
}
bounceables.push_back(tempbounce);
}
DPhysicsBounceable* DPhysicsManager::CreateBounceable(ISceneManager* smgr, core::vector3df position)
{
ITexture* metalcube=device->getVideoDriver()->getTexture("metal2.jpg");
ISceneNode* bouncetest=NULL;
bouncetest=smgr->addTestSceneNode(20.0f,0,-1, position);
bouncetest->setMaterialTexture(0,metalcube);
DPhysicsBounceable* b=new DPhysicsBounceable(theWorld,theSpace,0,0,bouncetest,NULL, true);
b->setGeomData();
// b->timetolive=now + LIVETIME*frand();
bounceables.push_back(b);
return b;
}
void DPhysicsManager::updateEntitiesAfterPhysics(){
irr::core::vector3df pos;
irr::core::vector3df rot;
std::list<DPhysicsBounceable*>::iterator iter=NULL;
for(iter=bounceables.begin();iter!=bounceables.end();++iter){
DPhysicsBounceable* entity=(*iter);
dGeomID geom=entity->geom;
if(geom!=0){
if(entity->updatephysics)
{
// get the new position of the ODE geometry
dReal* ode_pos=(dReal*)dGeomGetPosition(geom);
// set the position at the scenenode
pos.set((irr::f32)ode_pos[0],(irr::f32)ode_pos[1],(irr::f32)ode_pos[2]);
entity->node->setPosition(pos);
// get the rotation quaternion
dQuaternion result;
dGeomGetQuaternion(geom, result);
// convert it to eulerangles
QuaternionToEuler(result,rot);
// set the rotation
entity->node->setRotation(rot);
}
}
}
}
void DPhysicsManager::nearCollisionCallback(void* data, dGeomID o1, dGeomID o2)
{
int i=0;
dBodyID b1=dGeomGetBody(o1);
dBodyID b2=dGeomGetBody(o2);
if(b1 && b2 && dAreConnectedExcluding(b1,b2,dJointTypeContact))return;
dContact contact[MAX_CONTACTS];
for(i=0;i<MAX_CONTACTS;i++){
contact[i].surface.mode =dContactBounce |
dContactSoftERP | dContactSoftCFM;
contact[i].surface.mu = dInfinity;
contact[i].surface.slip1 = 0.1; // friction
contact[i].surface.slip2 = 0.1;
contact[i].surface.bounce=0.2f;
contact[i].surface.soft_erp = 0.2f;
contact[i].surface.soft_cfm = 0.2f;
}
int numc=dCollide(o1,o2,MAX_CONTACTS,&contact[0].geom,sizeof(dContact));
if(numc>0){
/*
// do something usefull with the colliding objects
if(o1) {
Bounceable* bounce1=(Bounceable*)dGeomGetData(o1);
}
if(o2) {
Bounceable* bounce2=(Bounceable*)dGeomGetData(o2);
}
*/
for(i=0;i<numc;i++){
dJointID c = dJointCreateContact(theWorld,theJointGroup,contact+i);
dJointAttach(c,b1,b2);
}
}
}
void DPhysicsManager::QuaternionToEuler(const dQuaternion quaternion,irr::core::vector3df &euler){
dReal w,x,y,z;
w=quaternion[0];
x=quaternion[1];
y=quaternion[2];
z=quaternion[3];
double sqw = w*w;
double sqx = x*x;
double sqy = y*y;
double sqz = z*z;
// heading
euler.Z = (irr::f32) (atan2(2.0 * (x*y + z*w),(sqx - sqy - sqz + sqw))*irr::core::GRAD_PI);
// bank
euler.X = (irr::f32) (atan2(2.0 * (y*z + x*w),(-sqx - sqy + sqz + sqw))*irr::core::GRAD_PI);
// attitude
euler.Y = (irr::f32) (asin(-2.0 * (x*z - y*w))*irr::core::GRAD_PI);
}
void DPhysicsManager::EulerToQuaternion(const irr::core::vector3df &euler, dQuaternion quaternion){
double _heading=euler.Z*irr::core::GRAD_PI2/2.0;
double _attitude=euler.Y*irr::core::GRAD_PI2/2.0;
double _bank=euler.X*irr::core::GRAD_PI2/2.0;
double c1 = cos(_heading);
double s1 = sin(_heading);
double c2 = cos(_attitude);
double s2 = sin(_attitude);
double c3 = cos(_bank);
double s3 = sin(_bank);
double c1c2 = c1*c2;
double s1s2 = s1*s2;
//w
quaternion[0]=(dReal) (c1c2*c3 + s1s2*s3);
//x
quaternion[1]=(dReal) (c1c2*s3 - s1s2*c3);
//y
quaternion[2]=(dReal) (c1*s2*c3 + s1*c2*s3);
//z
quaternion[3]=(dReal) (s1*c2*c3 - c1*s2*s3);
}
DPhysicsManager.h
Code: Select all
#ifndef DPHYSICSMANAGER_H
#define DPHYSICSMANAGER_H
#include <list>
#include <ode/ode.h>
#include <irrlicht.h>
#include "DPhysicsBounceable.h"
#include "DCollisionInterface.h"
#pragma comment(lib,"ode.lib")
#pragma comment(lib,"irrlicht.lib")
// gravitation force y-direction
#define GRAVITY -1.0f
#define QUBE_SPACING 50.0f
#define LIVETIME 5000
#define QUBE_INITHEIGHT 7000
#define QUBE_SIZE 150.0f
// maximum number of qubes in the simulation
#define MAX_QUBES 200
// maximum amount of contact joints the simulation will create for each collision
#define MAX_CONTACTS 64
class DPhysicsManager{
public:
// ODE Part
std::list<DPhysicsBounceable*> bounceables;
static dWorldID theWorld; // just one world for now
static dSpaceID theSpace; // and one space for all objects
static dJointGroupID theJointGroup; // the group to which all contact joint are added
// for mesh geometry collisions
dVector3 *vertices; // vertex array for trimesh geom
int *indices; // index array for trimesh geom
int vertexcount; // number of vertices in the vertex array
int indexcount; // number of indices in the index array
u32 timetolive;
u32 now;
DPhysicsBounceable* bounce;
core::vector3df pos;
// End of ODE Part
DPhysicsManager(IrrlichtDevice *device);
~DPhysicsManager();
void init();
void addEntity(DCollisionInterface *entity, bool box, bool test, bool updatephysics);
DPhysicsBounceable* CreateBounceable(ISceneManager* smgr, core::vector3df position);
void updatePhysics();
static void nearCollisionCallback(void* data, dGeomID o1, dGeomID o2);
void updateEntitiesAfterPhysics();
void QuaternionToEuler(const dQuaternion quaternion,irr::core::vector3df &euler);
void EulerToQuaternion(const irr::core::vector3df &euler, dQuaternion quaternion);
private:
IrrlichtDevice *device;
};
#endif
DPhysicsBounceable.cpp
Code: Select all
#include "DPhysicsBounceable.h"
DPhysicsBounceable::DPhysicsBounceable(dWorldID w, dSpaceID s, dBodyID b, dGeomID g, ISceneNode* n, IMesh* m, bool updatephysics):world(w),space(s),body(b),geom(g),node(n),mesh(m),indexcount(0),vertexcount(0),indices(NULL),vertices(NULL), updatephysics(updatephysics)
{
}
DPhysicsBounceable::~DPhysicsBounceable()
{}
void DPhysicsBounceable::setGeomData(){
irr::core::aabbox3d<irr::f32> box=node->getBoundingBox();
irr::core::vector3df extend=box.getExtend();
irr::core::vector3df pos=node->getPosition();
geom=dCreateBox(space,(dReal)extend.X,(dReal)extend.Y,(dReal)extend.Z);
dGeomSetData(geom,(void*)this);
dGeomSetPosition(geom,pos.X,pos.Y,pos.Z);
// create a body for this object
body=dBodyCreate(world);
// set mass shape
dMassSetBox(&mass,1.0f,(dReal)extend.X,(dReal)extend.Y,(dReal)extend.Z);
// adjust mass weight
dMassAdjust(&mass,0.25);
// make the body have a mass
dBodySetMass(body,&mass);
// add the body to the geom
dGeomSetBody(geom,body);
dBodySetPosition(body,pos.X,pos.Y,pos.Z);
dBodySetData(body,(void*)this);
}
void DPhysicsBounceable::setGeomData(IMesh* m){
if(mesh==NULL || node==NULL) return; // do nothing if the mesh or node is NULL
int i,j,ci,cif,cv;
indexcount=0;
vertexcount=0;
// count vertices and indices
//for(i=0;i<m->getMeshBufferCount();i++){
IMeshBuffer* mb=mesh->getMeshBuffer(0);
indexcount+=mb->getIndexCount();
vertexcount+=mb->getVertexCount();
std::cout << indexcount << std::endl;
std::cout << vertexcount << std::endl;
//}
// build structure for ode trimesh geom
vertices=new dVector3[vertexcount];
indices=new int[indexcount];
// fill trimesh geom
ci=0;
cif=0;
cv=0;
for(i=0;i<mesh->getMeshBufferCount();i++){
irr::scene::IMeshBuffer* mb=mesh->getMeshBuffer(i);
// fill indices
irr::u16* mb_indices=mb->getIndices();
for(j=0;j<mb->getIndexCount();j++){
indices[ci]=cif+mb_indices[j];
ci++;
}
cif=cif+mb->getVertexCount();
// fill vertices
if(mb->getVertexType()==irr::video::EVT_STANDARD){
irr::video::S3DVertex* mb_vertices=(irr::video::S3DVertex*)mb->getVertices();
for(j=0;j<mb->getVertexCount();j++){
vertices[cv][0]=mb_vertices[j].Pos.X;
vertices[cv][1]=mb_vertices[j].Pos.Y;
vertices[cv][2]=mb_vertices[j].Pos.Z;
cv++;
}
}else if(mb->getVertexType()==irr::video::EVT_2TCOORDS){
std::cout << "Type : EVT_2TCOORDS" << std::endl;
irr::video::S3DVertex2TCoords* mb_vertices=(irr::video::S3DVertex2TCoords*)mb->getVertices();
for(j=0;j<mb->getVertexCount();j++){
vertices[cv][0]=mb_vertices[j].Pos.X;
vertices[cv][1]=mb_vertices[j].Pos.Y;
vertices[cv][2]=mb_vertices[j].Pos.Z;
cv++;
}
}
}
irr::core::vector3df pos=node->getPosition();
// build the trimesh data
dTriMeshDataID data=dGeomTriMeshDataCreate();
//dGeomTriMeshDataBuildSimple(data,(dReal*)vertices,vertexcount,indices,indexcount);
dGeomTriMeshDataBuildSimple(data,(dReal*)vertices,vertexcount,indices,indexcount);
// build the trimesh geom
geom=dCreateTriMesh(space,data,0,0,0);
// set the geom position
dGeomSetPosition(geom,pos.X,pos.Y,pos.Z);
// lets have a pointer to our bounceable, we could need this in the collision callback
dGeomSetData(geom,(void*)this);
// in our application we don't want geoms constructed with meshes (the terrain) to have a body
dGeomSetBody(geom,0);
}
void DPhysicsBounceable::setGeomDataTest(){
irr::core::aabbox3d<irr::f32> box=node->getBoundingBox();
irr::core::vector3df extend=box.getExtend();
irr::core::vector3df pos=node->getPosition();
geom=dCreateBox(space,(dReal)extend.X,(dReal)extend.Y,(dReal)extend.Z);
dGeomSetData(geom,(void*)this);
dGeomSetPosition(geom,pos.X,pos.Y,pos.Z);
// create a body for this object
body=dBodyCreate(world);
// set mass shape
dMassSetBox(&mass,1.0f,(dReal)extend.X,(dReal)extend.Y,(dReal)extend.Z);
// adjust mass weight
dMassAdjust(&mass,0.25);
// make the body have a mass
dBodySetMass(body,&mass);
// add the body to the geom
dGeomSetBody(geom,0);
//dBodySetPosition(body,pos.X,pos.Y,pos.Z);
//dBodySetData(body,(void*)this);
}
DPhysicsBounceable.h
Code: Select all
#ifndef DPHYSICSBOUNCEABLE_H
#define DPHYSICSBOUNCEABLE_H
#include <iostream>
#include <list>
#include <ode/ode.h>
#include <irrlicht.h>
#pragma comment(lib,"ode.lib")
#pragma comment(lib,"irrlicht.lib")
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
class DPhysicsBounceable{
public:
dWorldID world; // the world to which the object will belong to
dSpaceID space; // the objects space
dBodyID body; // body data of the object
dGeomID geom; // geometry data of the object
dMass mass; // mass of the object
// for mesh geometry collisions
dVector3 *vertices; // vertex array for trimesh geom
int *indices; // index array for trimesh geom
int vertexcount; // number of vertices in the vertex array
int indexcount; // number of indices in the index array
u32 timetolive;
ISceneNode* node; // visualizing node
IMesh* mesh; // mesh of the visualizing node
DPhysicsBounceable(dWorldID w, dSpaceID s, dBodyID b, dGeomID g, ISceneNode* n, IMesh* m, bool updatephysics);
~DPhysicsBounceable();
void setGeomData();
void setGeomData(IMesh* m);
void setGeomDataTest();
bool updatephysics;
private:
};
#endif
And a interface-class
DCollisionInterface.h
Code: Select all
#ifndef DCOLLISIONINTERFACE_H
#define DCOLLISIONINTERFACE_H
#include <irrlicht.h>
#pragma comment(lib,"irrlicht.lib")
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
class DCollisionInterface
{
public:
virtual IMesh *getMesh() = 0;
virtual ISceneNode *getSceneNode() = 0;
};
#endif
Makefile.win :
Code: Select all
CPP = g++
OPTS = -I"C:\irrlicht\include" -I"c:/MinGW/include/c++/3.2.3" -I"C:\irrlicht\source" -I"..\..\common" -I"C:\OpenAL\Include" -I"C:\ode\include"
OPTSL = -L"C:\irrlicht\bin\DevCpp" -lIrrlicht -L"C:\OpenAL\libs" -lalut -lopenal32 -L"C:\ode\lib" -lode
BINNAME = example.exe
Makefile.linux :
Code: Select all
CPP = g++
CCFLAGS = -O2
OPTS = -I"../../libs/linux/irrlicht/include/" -I"/usr/X11R6/include" -I"../../libs/linux" -I"../../common/" -I"../../libs/linux/openal/include/" -I"../../libs/linux/ode/include"
OPTSL = -L"/usr/X11R6/lib" -L"../../libs/linux/irrlicht/lib/Linux/" -L"../../libs/linux/ace" -L"../../libs/linux/openal/lib" -L"../../libs/linux/ode/lib" -lIrrlicht -lGL -lXxf86vm -lXext -lX11 -lz -ljpeg -lACE -lopenal -lode
BINNAME = example
include Makefile.common
The physicsmanager is used this way :
physicsmanager = new DPhysicsManager(device);
// this adds my terrain.. i didnt clean addEntity up so there are still unnecessary parameters... have a look at the code and you'll see...
physicsmanager->addEntity(smallMap, false, false, false);
// this creates a crate at the given position
physicsmanager->CreateBounceable(smgr, core::vector3df(320,100,460));
P.S. : Big THX to Zola for helping
