#include <irrlicht.h>
#include <iostream>
#include <ode/ode.h>
using namespace irr;
//Parramètre de notre physique
dWorldID world = dWorldCreate();
dSpaceID space = dHashSpaceCreate (0);
dJointGroupID contactgroup = dJointGroupCreate (0);
dMass mass;
dBodyID body;
dGeomID geom;
//Proto de la méthode nearCallback
void nearCallback (void *data, dGeomID o1, dGeomID o2);
//Proto de la méthode updateEntitiesAfterPhysics
void updateEntitiesAfterPhysics(scene::ISceneNode* irrSceneNode,dGeomID geom);
//Proto de la méthode BtnClicked
bool BtnClicked(gui::IGUIButton* buton);
int main()
{
//Creation du Device
IrrlichtDevice* device = createDevice(video::EDT_OPENGL, core::dimension2d<s32>(800, 600),32,false,false,true);
if (device == 0)
return 1; // Si on ne peut pas creer le device avec ses drivers on quitte
//On récupère les objets pour simplifier le code plus tard
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
gui::IGUIEnvironment* env = device->getGUIEnvironment();
//Chargement des médias
video::ITexture* Tex = driver->getTexture("../media/wall.jpg");
video::ITexture* Tex2 = driver->getTexture("../media/wall.bmp");
//Modification d'une texture
Tex->getTransformation().setScale(core::vector3df(300,300,300));
//Creation de la camera
scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(NULL,core::vector3df(30,40,-82),core::vector3df(0,3,0),-1);
//creation du mesh 3D de la sphere
scene::ISceneNode* SphereSceneNode = smgr->addSphereSceneNode(5,32,NULL,1,core::vector3df(0,5.0f,-55));
SphereSceneNode->setMaterialTexture(0,Tex2);
SphereSceneNode->setMaterialFlag(video::EMF_LIGHTING,false);
//Creation d'un plan 3D physique
dCreatePlane (space,0,1,0,0);
//initialisation des Body et Geom physique
dBodyID Body[83];
dGeomID Box[81];
dGeomID Sphere;
//Creation du body physique de la sphere
Body[82] = dBodyCreate (world);
dMassSetSphereTotal(&mass,6.0f,5);
dBodySetMass(Body[82],&mass);
dBodySetPosition(Body[82],0,5,-55);
//Creation de la géométrie physique de la sphere
Sphere = dCreateSphere(space, 5);
dGeomSetBody(Sphere,Body[82]);
//On va parramétrer la physique en même temps que la création de tout les cubes
scene::ISceneNode* CubeSceneNode[81];
s32 NbsCubeSceneNode=0;
for (f32 y=0.0f;y<=40.0f;y+=5.0f)
{
for(f32 x=-40.0f+y;x<=40.0f-y;x+=5.0f)
{
//Creation des meshs des cubes
CubeSceneNode[NbsCubeSceneNode] = smgr->addCubeSceneNode(5,NULL,-1,core::vector3df(x,y+2.5f,0));
CubeSceneNode[NbsCubeSceneNode]->setMaterialTexture(0,Tex2);
CubeSceneNode[NbsCubeSceneNode]->setMaterialFlag(video::EMF_LIGHTING,false);
//Creation du Body physique des cubes
Body[NbsCubeSceneNode] = dBodyCreate(world);
dMassSetBoxTotal(&mass,5,5,5,5);
dBodySetPosition(Body[NbsCubeSceneNode],x,y+2.5f,0);
dBodySetAutoDisableFlag(Body[NbsCubeSceneNode],true);
dBodyDisable(Body[NbsCubeSceneNode]);
dBodySetAutoDisableLinearThreshold(Body[NbsCubeSceneNode],0.2);
dBodySetAutoDisableAngularThreshold(Body[NbsCubeSceneNode],0.2);
//Creation de la géométrie physique des cubes
Box[NbsCubeSceneNode] = dCreateBox(space,5,5,5);
dGeomSetBody(Box[NbsCubeSceneNode],Body[NbsCubeSceneNode]);
//Incrémentation de la variable NbsCubeSceneNode
NbsCubeSceneNode++;
}
}
//On creer un mesh cubique qu'on applatira par le scale pour le sol
scene::ISceneNode* SolSceneNode = smgr->addCubeSceneNode(10000,NULL,-1,core::vector3df(0,0,0));
SolSceneNode->setMaterialTexture(0,Tex);
SolSceneNode->setMaterialFlag(video::EMF_LIGHTING,false);
SolSceneNode->setScale(core::vector3df(1,0.0f,1));
//On ajoute un boutton pour expulser notre boule
gui::IGUIButton* Buton = env->addButton(core::rect<s32>(10,10,120,35),NULL,100,L"Boum");
//initialise la variable lastFPS
int lastFPS = -1;
//On parramètre la gravité de la physique et le nombre de frame qu'elle va saute pour les calculs
dWorldSetGravity (world,0,-3,0);
//Boucle principale
while(device->run())
if (device->isWindowActive())
{
//Calcul de notre physique
dSpaceCollide (space, 0, &nearCallback);
dWorldStepFast1(world,0.04f,1);
dJointGroupEmpty(contactgroup);
//On met à jour la boule en position + rotation grace à notre physique
updateEntitiesAfterPhysics(SphereSceneNode,Sphere);
//On met à jour les cubes en position + rotation grace à notre physique
for (int i=0; i<81; i++)
{
updateEntitiesAfterPhysics(CubeSceneNode[i],Box[i]);
}
//début des calculs d'irrlicht
driver->beginScene(true, true, video::SColor(10,10,10,10));
//On test si le bouton de la GUI est appuyé
if (BtnClicked(Buton))
{
dBodyAddForce(Body[82],0,3000,10000);
}
//On dessine tout
smgr->drawAll();
//On dessine la GUI
env->drawAll();
//Fin des calculs d'irrlicht
driver->endScene();
//On récupère le FPS dans la variable fps
int fps = driver->getFPS();
//On affiche le fps dans la barre de titre
if (lastFPS != fps)
{
core::stringw str = L"Irrlicht Engine with ODE [";
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
//On supprime le device d'irrlicht
device->drop();
//On supprime les groupes de joints de Ode
dJointGroupEmpty (contactgroup);
dJointGroupDestroy (contactgroup);
//On supprime la géométrie de la sphère
dGeomDestroy (Sphere);
//On supprime les géométries des cubes
for (int i=0;i<81;i++)
{
dGeomDestroy(Box[i]);
}
//On détruit l'espace d'ode
dSpaceDestroy (space);
//On détruit le monde d'ode
dWorldDestroy (world);
//retour 0 pour quitter
return 0;
}
//La mise à jour de la physique se fait aussi par la mise à jour des objets
void updateEntitiesAfterPhysics(scene::ISceneNode* irrSceneNode,dGeomID geom)
{
//Ici on met à jour la position
irrSceneNode->setPosition(core::vector3df((f32)dGeomGetPosition(geom)[0],(f32)dGeomGetPosition(geom)[1],(f32)dGeomGetPosition(geom)[2]));
//Un quaternion Ode pour les calculs de rotations
dQuaternion result;
//On écrit les rotations de ode dans le quaternion result
dGeomGetQuaternion(geom,result);
//Un quaternion Irrlicht pour convertir le quaternion de Ode
core::quaternion quat;
//On converti le quaternion de ode en quaternion Irrlicht
quat.W = result[0];
quat.X = result[1];
quat.Y = result[2];
quat.Z = result[3];
//Vecteur Rot pour le calcul de la rotation
core::vector3df Rot;
//On bascule notre quaternion en vecteur d'angle euler
quat.toEuler(Rot);
//On converti de radian vers degré
Rot.X = Rot.X / 3.14f * 180;
Rot.Y = Rot.Y / 3.14f * 180;
Rot.Z = Rot.Z / 3.14f * 180;
//Ici on met à jour la rotation
irrSceneNode->setRotation(Rot);
}
//Le fameux callBack de calcul pour la physique (merci les samples de ode)
void nearCallback (void *data, dGeomID o1, dGeomID o2)
{
assert(o1);
assert(o2);
if (dGeomIsSpace(o1) || dGeomIsSpace(o2))
{
//Y'a-t-il une collision dans l'espace
dSpaceCollide2(o1,o2,data,&nearCallback);
return;
}
const int N = 32;
dContact contact[N];
int n = dCollide (o1,o2,N,&(contact[0].geom),sizeof(dContact));
if (n > 0)
{
for (int i=0; i<n; i++)
{
contact[i].surface.slip1 = 0.7;
contact[i].surface.slip2 = 0.7;
contact[i].surface.mode = dContactSoftERP | dContactSoftCFM | dContactApprox1 | dContactSlip1 | dContactSlip2;
contact[i].surface.mu = 50.0; // was: dInfinity
contact[i].surface.soft_erp = 0.97;
contact[i].surface.soft_cfm = 0.2;
dJointID c = dJointCreateContact (world,contactgroup,&contact[i]);
dJointAttach (c,dGeomGetBody(contact[i].geom.g1),dGeomGetBody(contact[i].geom.g2));
}
}
}
//Si on veut pas passer par les events ont peut bidouiller se genre de méthode
bool BtnClicked(gui::IGUIButton* buton)
{
static bool AncienClic;
if (AncienClic==true && buton->isPressed()==false)
{
AncienClic=buton->isPressed();
return true;
}
else
{
AncienClic=buton->isPressed();
return false;
}
}