This is the first tutorial on using Irrlicht with PAL. At the end of this tutorial you will be able to drop a number of objects on to a terrain.
To begin with, please ensure that you have PAL installed and compiled, and that you can compile the Irrlicht terrain tutorial. We will be extending the TerrainRendering example.
The first thing to do used is to add the PAL initialisation code to Irrlicht. To do this, insert the following code into the begining of the main function:
//load physics
PF->LoadPALfromDLL();
//let user select physics driver, we will just set it to use bullet for now.
PF->SelectEngine("Bullet");
//set up physics
palPhysics *pp = PF->CreatePhysics();
if (pp)
pp->Init(0,-9.8,0);
We also need to include the PAL library, do this by adding the header file at the top of the main program:
#include "pal/palFactory.h"
#pragma comment(lib, "libpal.lib") //only for MSVC!
Finally, copy the pal DLL files to the Irrlicht Bin directory (e.g. Copy "bin\Win32-VisualStudio"
Before we can compile this, we will need to include PAL settings to our configuration. (The include and library paths). If you are using Microsoft Visual Studio, you can do this by right clicking on the project, selecting "Properties", then "C/C++", "General", and modifying the "Additional Include Directories" to include PAL. (eg: "C:\lib\pal"). The irrlicht example must also be made compatible with DLL's, so the "Code Generation" option needs to be set to "Multithreaded DLL". We also need to set the library path. Select "Linker" and "Additional Library Directories", add e.g. "C:\lib\pal\lib\release\vs2005".
You should now have a program that compiles and runs - but will do nothing. We still need to glue the PAL code and Irrlicht code together.
^ topTo connect Irrlicht with PAL we need to copy the information from the physics engine (eg: position) to Irrlicht's nodes. We can do this by making a "Bind" class.
class BindObject {
public:
//the Irrlicht Node
irr::scene::ISceneNode *node;
//the PAL "Node"
palBodyBase *pb;
//update Irrlicht position from physics
void Update() {
//get the location matrix from the physics system
palMatrix4x4 matrix = pb->GetLocationMatrix();
//copy it to a Irrlicht matrix
core::matrix4 mat;
memcpy(&mat[0], matrix._mat, sizeof(f32)*4*4);
//set the node position from our Irrlicht matrix
node->setPosition(mat.getTranslation());
node->setRotation(mat.getRotationDegrees());
}
};
Lets test out our new class by creating some cubes. First, we will keep a array of all the objects that we create. This way we know what to update.
Then we can create the Irrlicht scene node, and a corresponding physics box. Finally, we add this to our vector.
//our vector of all physics objects
std::vector<BindObject *> vbo;
//lets make 7 cubes
for (int i=1;i<7;i++) {
BindObject *pbo = 0;
float size = 300.0f;
//create a new bind object
pbo = new BindObject;
//lets set the bind object node to a cube scene node
pbo->node = smgr->addCubeSceneNode(size,0,-1,core::vector3df(0,0,0),core::vector3df(0,0,0),core::vector3df(1,1,1));
//lets create a physics box as well
palBox *pb = PF->CreateBox();
//set the physics position, and a matching size
pb->Init(size*i*5,size*5,size*i*5, size,size,size, 10);
//assing the bind object body to the physics box
pbo->pb = pb;
//add this to our vector of objects
vbo.push_back(pbo);
}
Now we have everything to display and calculate, but we still have not told the physics to actually calculate anything. To do that, we need to add some code to the main "while(device->run())" loop.
First, we tell the physics engine to increment the time step. This tells the physics engine how much time has passed, and will cause it to calculate the new positions of all our objects.
After this, we will loop through our array of objects, and update the positions.
//update physics
if (pp)
pp->Update(1/60.0f); //timestep
//update the irrlicht graphics to be at the same location as the physics engine
for (i=0;i<vbo.size();i++) {
vbo[i]->Update();
}
Now we have something ready to run!
But the cubes fall through the ground! Oh no! We still need to add the terrain.
^ topNow we will be adding the terrain to the physics engine. So we will not to be needing the Irrlicht terrain collision system (remove from "ITriangleSelector" to "anim->drop()"). Now we will add our own function that will place the terrain at our own desired location and scale.
To begin with we call Irrlichts "addTerrainSceneNode" function. Then, we get access to the buffer that has the vertex and index information for the terrain. This data is then extracted, and passed into the physics engine via the "ptm->Init" call.
void LoadTerrain(float x, float y, float z, float sx, float sy, float sz) {
//call the irrlicht add terrain scene node
g_terrain = g_smgr->addTerrainSceneNode(
"../../media/terrain-heightmap.bmp",
0, // parent node
-1, // node id
core::vector3df(x, y, z), // position
core::vector3df(0.f, 0.f, 0.f), // rotation
core::vector3df(sx, sy, sz), // scale
video::SColor ( 255, 255, 255, 255 ), // vertexColor,
5, // maxLOD
scene::ETPS_17, // patchSize
4 // smoothFactor
);
//get the vertex buffer
scene::SMeshBufferLightMap smb;
g_terrain->getMeshBufferForLOD(smb,0);
//get the number of vertices and indicies from Irrlicht
int nv = smb.getVertexCount ();
int ni = smb.getIndexCount ();
//create the physics terrain mesh, and some space to store our data
palTerrainMesh *ptm = PF->CreateTerrainMesh();
float *pVerts = new float[3*nv];
int *pInds = new int[ni];
//get a pointer to the verticies and indicies from Irrlicht
irr::video::S3DVertex2TCoords* pv = (irr::video::S3DVertex2TCoords*) smb.getVertices();
irr::u16* pi = (irr::u16*)smb.getIndices();
//copy the vertex and index data
int i;
for (i=0;i<nv;i++) {
pVerts[i*3+0]=sx*pv[i].Pos.X + x; //scale each vertex by sX and set its position with x
pVerts[i*3+1]=sy*pv[i].Pos.Y + y;
pVerts[i*3+2]=sz*pv[i].Pos.Z + z;
}
for (i=0;i<ni;i++) {
pInds[i]=pi[i];
}
//initialize the physics terrain
ptm->Init(0,0,0,pVerts,nv,pInds,ni);
delete []pVerts;
delete []pInds;
}
Now lets call our function:
LoadTerrain(0,0,0,
40,4.4,40);
When you run the program, you will see the cubes are now bouncing off the terrain. However, the scale we are using is not suitable for all physics engines, so lets resize our world to be a bit more realistic.
scene::ICameraSceneNode* camera =
g_smgr->addCameraSceneNodeFPS(0,100.0f,12.0f);
camera->setPosition(core::vector3df(0,15,10));
camera->setTarget(core::vector3df(10,15,10));
camera->setFarValue(120.0f);
...
LoadTerrain(-100,0,-100,
0.8,0.04,0.8);
...
float size = 2.0f;
Finally, lets take advantage of PAL to let the user select a physics engine:
std::cin >> i;
switch(i)
{
case 'a': PF->SelectEngine("Bullet");break;
case 'b': PF->SelectEngine("Jiggle");break;
case 'c': PF->SelectEngine("Newton");break;
case 'd': PF->SelectEngine("ODE");break;
case 'e': PF->SelectEngine("SPE");break;
case 'f': PF->SelectEngine("Tokamak");break;
case 'g': PF->SelectEngine("TrueAxis");break;
default: return 1;
}