Initialising
// Init newton
nWorld = NewtonCreate(NULL, NULL);
// Set up default material properties for newton
int i = NewtonMaterialGetDefaultGroupID(nWorld);
NewtonMaterialSetDefaultFriction (nWorld, i, i, 0.8f, 0.4f);
NewtonMaterialSetDefaultElasticity (nWorld, i, i, 0.3f);
NewtonMaterialSetDefaultSoftness (nWorld, i, i, 0.05f);
NewtonMaterialSetCollisionCallback (nWorld, i, i, NULL, NULL, NULL, NULL);
To start off, we create the newton object with NewtonCreate().
Next, we set the default properties for materials. Materials
define how objects interact when they collide and you can
use different materials to simulate ice, wood, sand, etc.
In this example we will just create all our objects with the
default material.
Newton Callbacks
Newton will call back to staticly definied functions in your
code to tell you information such as when an
object is moving. I will show how to assign the events to bodies
further down the page.
void _cdecl CGame::SetMeshTransformEvent(const NewtonBody* body, const float* matrix)
{
// copy the matrix into an irrlicht matrix4
matrix4 mat;
memcpy(mat.M, matrix, sizeof(float)*16);
// Retreive the user data attached to the newton body
ISceneNode *tmp = (ISceneNode *)NewtonBodyGetUserData(body);
if (tmp)
{
// Position the node
tmp->setPosition(mat.getTranslation()); // set position
tmp->setRotation(mat.getRotationDegrees()); // and rotation
}
}
This function will be called by newton bodies when they move.
By storing an ISceneNode pointer in the userData
of the body, we can position the corresponding scenenode easily.
void _cdecl CGame::ApplyForceAndTorqueEvent (const NewtonBody* body)
{
float mass;
float Ixx;
float Iyy;
float Izz;
float force[3];
float torque[3];
NewtonBodyGetMassMatrix (body, &mass, &Ixx, &Iyy, &Izz);
force[0] = 0.0f;
force[1] = NEWTON_GRAVITY * mass;
force[2] = 0.0f;
torque[0] = 0.0f;
torque[1] = 0.0f;
torque[2] = 0.0f;
NewtonBodyAddForce (body, force);
NewtonBodyAddTorque (body, torque);
}
This function is called by the Newton Engine every time an
active body is going to be simulated. We add gravity
to our bodies by means of a downwards force.
Setting up the scene
g_newtonmap = NewtonCreateTreeCollision(nWorld, NULL);
NewtonTreeCollisionBeginBuild(g_newtonmap);
int cMeshBuffer, j;
int v1i, v2i, v3i;
IMeshBuffer *mb;
float vArray[9]; // vertex array (3*3 floats)
int tmpCount = 0;
for (cMeshBuffer=0; cMeshBuffergetMesh(0)->getMeshBufferCount(); cMeshBuffer++)
{
mb = g_map->getMesh(0)->getMeshBuffer(cMeshBuffer);
video::S3DVertex2TCoords* mb_vertices = (irr::video::S3DVertex2TCoords*)mb->getVertices();
u16* mb_indices = mb->getIndices();
// add each triangle from the mesh
for (j=0; jgetIndexCount(); j+=3)
{
v1i = mb_indices[j];
v2i = mb_indices[j+1];
v3i = mb_indices[j+2];
vArray[0] = mb_vertices[v1i].Pos.X;
vArray[1] = mb_vertices[v1i].Pos.Y;
vArray[2] = mb_vertices[v1i].Pos.Z;
vArray[3] = mb_vertices[v2i].Pos.X;
vArray[4] = mb_vertices[v2i].Pos.Y;
vArray[5] = mb_vertices[v2i].Pos.Z;
vArray[6] = mb_vertices[v3i].Pos.X;
vArray[7] = mb_vertices[v3i].Pos.Y;
vArray[8] = mb_vertices[v3i].Pos.Z;
NewtonTreeCollisionAddFace(g_newtonmap, 3, (float*)vArray, 12, 1);
}
}
NewtonTreeCollisionEndBuild(g_newtonmap, 0);
g_newtonmapbody = NewtonCreateBody(nWorld, g_newtonmap);
After loading the bsp mesh in irrlicht we must create a newton
collision tree consisting of all the triangles
in the bsp mesh. To do this we loop through the indices list
and add vertices 3 at a time into newton. I should
mention here that I was lazy when writing this and just used
a straight cast to video::S3DVertex2TCoords - this is because
bsp meshes have lightmap textures on them. If you are using
a mesh with only 1 set of texture co-ordinates (like
a landscape or .x level) then use (video::S3DVertex).
Newton requires 2 pointers for every object - a NewtonBody*
for the rigid body, and a NewtonCollision* that
describes the objects geometry. After creating the tree collision
geometry, we create a body for the map and
assign it to the collision. Bodys which have a tree collision
for their geometry are static and their mass
will be ignored by newton - so you can only use meshes for your
scenery!
// set the newton world size based on the bsp size
float boxP0[3];
float boxP1[3];
float matrix[4][4];
NewtonBodyGetMatrix (g_newtonmapbody, &matrix[0][0]);
NewtonCollisionCalculateAABB (g_newtonmap, &matrix[0][0], &boxP0[0], &boxP1[0]);
// you can pad the box here if you wish
//boxP0.y -= somevalue;
//boxP1.y += somevaluef;
NewtonSetWorldSize (nWorld, (float*)boxP0, (float*)boxP1);
After loading the map, we can calculate the bounding box
surrounding it and set the newton world size to
match. Newton should automatically disable anything which
falls out of the map this way.
Creating Cubes
// Create a box primitive.
tmp->collision = NewtonCreateBox(nWorld, 38, 38, 38, NULL);
tmp->body = NewtonCreateBody(nWorld, tmp->collision);
// Set user data pointer to the scene node
NewtonBodySetUserData(tmp->body, tmp->node);
// Set body mass & inertia matrix
NewtonBodySetMassMatrix (tmp->body, 10.0f, 150.0f, 150.0f, 150.0f);
// Set the freeze threshhold to 1 unit (default is 0.01 but irrlight uses a large unit scale)
NewtonBodySetFreezeTreshold(tmp->body, 1.0, 1.0, 1.0, 1.0);
// Set callback functions for the body
NewtonBodySetTransformCallback(tmp->body, SetMeshTransformEvent);
NewtonBodySetForceAndTorqueCallback(tmp->body, ApplyForceAndTorqueEvent);
// Set the position of the body
matrix4 mat;
mat.setTranslation(loc);
NewtonBodySetMatrix(tmp->body, &mat.M[0]);
To start with we create a box primitive. Note that 38 is just
a value I used to match the cube model
in the examples size.
We set the cubes userData pointer to the ISceneNode related
to this cube - this is so that we can move the
scene node when newton tells us the body is moving.
Next, we set the mass. The moment of inertia for a box is given
by the expression
Ixx = M * (z * z + y * y) / 12
I have used a hard value of 150.0 for the inertia matrix,
but you could write some better code
here to automatically calculate the inertia matrix based on
box size, mass and how you want the weight
to be distributed inside the box.
Finally, set the transform and force callbacks of the newton
body to our callback functions, and set
the initial position of the body.
Making everything move
OK so we have our scene all set up - but we need to make everything
move!
A simple call to newtonUpdate() will do the trick :)
NewtonUpdate(nWorld, 0.01f);
In the example I call NewtonUpdate 100 times a second, with
a 0.01 second timestep to match.
You can update less often with a larger timestep but be aware
that the larger your timestep is, the less
accurate the collisions will become. I would recommend updating
between 50 to 100 times a second for a game.
Cleaning Up
// release the collision tree
NewtonReleaseCollision(nWorld, g_newtonmap);
// release the box primitives
for (int i=0; i<currentCube; i++)
NewtonReleaseCollision(nWorld, cubes[i]->collision);
// destory the newton world object
NewtonDestroy(nWorld);
I think the comments explain well enough ;)
Well thats it! I hope you've found this tutorial helpful. If
you didnt see the link at the top, you can download
the example source code & binary for this tutorial
HERE.
-Mercior