Irrlicht && Newton && IrrKlang

A forum to store posts deemed exceptionally wise and useful
Post Reply
Yustme
Posts: 107
Joined: Sat Dec 01, 2007 10:50 pm

Irrlicht && Newton && IrrKlang

Post by Yustme »

Hi,

I made a game with 2 spheres and I cut the most important and useful things out of it into a new project.

For this project I have used:
  • Irrlicht version 1.4 (from the download page, the official release);
    IrrKlang version 1.0.3;
    Newton version 1.53;
I made it in windows using visual studio 2005.

There are a lot of things covered in my project which could be useful in yours. To name a few:
  • Getting 2 specific colliding bodies;
    Playing a sound when that happens;
    Playing a sound during the game;
    3th person camera view;
But i'll only explain how to get the 2 specific colliding bodies and what you could do with that information, in this tutorial.

We declare 3 callback functions for Newton:

Code: Select all

// callback functions for the 2 colliding bodies.
static int beginCallback(const NewtonMaterial* material, const NewtonBody* body0, const NewtonBody* body1);
static int processCallback(const NewtonMaterial* material, const NewtonContact* contact);
static void endCallback(const NewtonMaterial* material);

And a struct:

Code: Select all

// struct for colliding bodies.
struct CollidingBodies
{
   const NewtonBody *colBody0, *colBody1;
   bool SphereWithSphere;
} geomCollision;

After that, we initialize the physics engine:

Code: Select all

void InitializePhysicsEngine()
{
	nWorld = NewtonCreate(NewtonAlloc, NewtonFree);
	
	// set the linear solver model for faster speed 
  NewtonSetSolverModel (nWorld, 1);

	// set the adaptive friction model for faster speed 
  NewtonSetFrictionModel (nWorld, 1);

	// Set up default material properties for newton
	int i = NewtonMaterialGetDefaultGroupID(nWorld);
	NewtonMaterialSetDefaultFriction   (nWorld, i, i, 0.4f, 0.6f);
	NewtonMaterialSetDefaultElasticity (nWorld, i, i, 0.75f);
	NewtonMaterialSetDefaultSoftness   (nWorld, i, i, 0.0f);
	NewtonMaterialSetCollisionCallback (nWorld, i, i, sphereOneBody, beginCallback, processCallback, endCallback);
	NewtonMaterialSetCollisionCallback (nWorld, i, i, sphereTwoBody, beginCallback, processCallback, endCallback);
}

In the physics initialization, we made 2 CollisionCallBacks for 2 spheres (sphereOneBody & sphereTwoBody).

This means that we want to keep track of whether they collide against "something". In my project I want to know if the 2 spheres are colliding against each other. That's why I made 2 CollisionCallBacks for both spheres.

Suppose you want to know if either sphere collided against a wall. Then you have to add for example, something like this in the physics engine initialization:

Code: Select all

NewtonMaterialSetCollisionCallback(nWorld, i, i, wallBody, beginCallback, processCallback, endCallback);

Note:
Just adding an extra line with "wallBody" isn't enough. You'll need to declare it like this for newton: NewtonBody* wallBody; And a scene node for this wallBody.


Ok, now lets move on to the interesting part, "how to know if 2 specific bodies collided".

In the beginCallback function, we save 2 bodies that may potentially collide against each other:

Code: Select all

// check if bodies are almost colliding
static int beginCallback(const NewtonMaterial* material, const NewtonBody* body0, const NewtonBody* body1)
{
   geomCollision.colBody0 = body0;
   geomCollision.colBody1 = body1;
   return true; // telling newton we accept this collision
}

We save the 2 bodies in our struct "CollidingBodies".

Note:
Always return true, or (in our case) the spheres will fall through the floor.


Now we process the 2 bodies in the processCallBack function:

Code: Select all

// process the collided bodies
static int processCallback(const NewtonMaterial* material, const NewtonContact* contact)
{
	if(geomCollision.colBody0 == sphereOneBody || geomCollision.colBody1 == sphereOneBody)
	{
		if(geomCollision.colBody0 == sphereTwoBody || geomCollision.colBody1 == sphereTwoBody)
		{
			geomCollision.SphereWithSphere = true;
		}
	}
	
	return true; // telling newton we accept this collision
}

So in processCallback we check if the colBody0 and colBody1 from the struct "CollidingBodies" contain the 2 spheres.

Because there is no telling which 2 colBody's contains which sphereBody, we check if colBody0 has either of the sphereBodies and then we check if colBody1 has the remaining sphereBody.

For example, the last code snippet could be changed to something like this:

Code: Select all

if(geomCollision.colBody0 == sphereOneBody && geomCollision.colBody1 == sphereTwoBody)
  geomCollision.SphereWithSphere = true;

if(geomCollision.colBody0 == sphereTwoBody && geomCollision.colBody1 == sphereOneBody)
  geomCollision.SphereWithSphere = true;

return true; // telling newton we accept this collision

Note:
Always return true, or (in our case) the spheres will fall through the floor.


Now lets assume the 2 spheres collided against each other and the boolean "SphereWithSphere" is set to true in our struct "CollidingBodies". In my case, I wanted to play a sound.

That's exactly what we gonna do in the endCallback function:

Code: Select all

// end the callback and play sound
static void endCallback(const NewtonMaterial* material)
{
	if(geomCollision.SphereWithSphere)
	{
		const char* filename;
		filename = "data/explosion.wav";
		engine->play3D(filename, vector3df(0.0,0.0,0.0));
		// set boolean to false or you'll hear the explosion.wav the whole time.
		geomCollision.SphereWithSphere = false;
	}
}

We first check if the boolean "SphereWithSphere" is true. If thats the case, a sound (in this case explosion.wav) will be played.

You can cut and paste the code inside the endCallback function, anywhere in your code. For example, in your while loop. But it's not recommended as the sound might be played a few seconds later which you probably don't want to.

The engine is the sound engine of irrKlang and is declared globally and initialized in the main like this:

Code: Select all

// global :
// irrKlang declaration
ISoundEngine* engine = NULL;

// in main() :
engine = createIrrKlangDevice();

In this line:

Code: Select all

engine->play3D(filename, vector3df(0.0,0.0,0.0));

The irrKlang engine will play a sound in the "world" depending on the vector3df() value. In this case, the sound is played very close to you.

Although i am not a pro (yet) in playing 3D sounds, but i don't think it can get any closer if the vector3df has "0.0 , 0.0, 0.0" as value.

You could also play the sound where the collision happens by getting the position of either sphere for example, like this:

Code: Select all

engine->play3D(filename, sphereOne->getPosition());

If the second argument of "play3D" function value is too high, you'll be unable to hear the sound. To be sure where exactly the sound is being played, simply print the second argument value to the console for example, like this:

Code: Select all

cout << sphereOne-getPosition() << endl;

Don't forget to compare it to your own "position" which is:

Code: Select all

camera->getPosition();

I'll post a link soon where the project can be downloaded from (waiting for rooly). He'll be hosting it on irrlicht.sourceforge.net. In order to reduce the amount of bandwidth, i removed the textures, sounds, libs and dll' from the project.

I'll take the time (right now) to explain what you need and where you should put it in your project directory. Of course, you can use your own sounds and textures, instead of the ones i used.

But for everyone who wants to run the project ASAP, here are the instructions:

Install or just paste the 3 engines in the root directory of D:\. Or change the paths in the property window of visual studio 2005. Make sure that the paths are exactly the same as mine (except for drive letter).

You can get in the property window by pressing the right mouse button on "CollidingBodiesNewton" which is under "Solution 'CollidingBodiesNewton' (project 1)" in the tree view.

Go to the folder "data" after you unpacked the project, and put there:
  • earth.bmp
    fontlucida.png
    terrain-texture.jpg
    irrlicht2_bk.jpg
    irrlicht2_dn.jpg
    irrlicht2_ft.jpg
    irrlicht2_rt.jpg
    irrlicht2_up.jpg
Which all can be found in the folder "media" in your irrlicht directory.

And put these sound files in the folder "data" too:
  • explosion.wav
    ophelia.mp3
Which can be found in the "media" folder of irrKlang sound engine.

Put the these libs and dll's:
  • irrKlang.dll
    irrKlang.lib
    ikpMP3.dll
    Irrlicht.dll
    Irrlicht.lib
    Newton.dll
    Newton.lib
In this directory:

CollidingBodiesNewton\CollidingBodiesNewton

Look for the files in these directories:

\irrlicht-1.4\bin\Win32-VisualStudio
\irrKlang-1.0.3\irrKlang-1.0.3\bin\win32-visualStudio
\NewtonSDK\sdk\dll

Ok, thats it :D

If you have any questions about the project, no matter how dump or silly you think it is, please ask your question!

I'll try my best to help you.

For those who are using macintosh or linux and (may) port this project to it, please share it with the community.

Thanks in advance!

[EDIT]
The link for the project:

http://irrlichtirc.g0dsoft.com/Yustme/C ... Newton.zip
Post Reply