That’s our “game”? Doubtely… So let’s make things move like in real world! Or just like that…
Requirements
First of all, go and get the
Newton GD files. And unpack it… right to the source directory of our project! That’s right!
I’m not insane and I’m aware you are going to put a lot of files in your project. But have no
fear - you may always add them to .gitignore and skip them from being tracked in your Git repo:
You are using Git, right?.. Now, you place the Newton GD sources in your project directory and change
your CMakeLists.txt file to look like this:
Try to compile your project - it should be just fine. And observe the power of CMake!
Gravity
Let’s start modifying our Irrlicht sample application. First of all, we will add some Newton headers:
The basic thing in the whole Newton GD library is NewtonWorld. That is what it means - the world, where
all the physics happen. It is something different from where we place our 3D models. And that should be
obvious - graphics are managed by Irrlicht and physics - by Newton. Those are totally different libraries.
So we need to tie those two so that graphics correspond to what happens in physical world.
First of all, we need to have a variable for our NewtonWorld. And since physics are handled by scripts too,
we need to have that variable close to our other objects - in the ScriptManager class.
There are two functions we need to bind to our NewtonBody:
The first one, transformCallback, is called whenever body changes its transform -
e. g. either position or rotation. This is a good place to synchronize our Irrlicht meshes’
positions with their Newton bodies.
The applyForceAndTorqueCallback function is called on each NewtonUpdate to set the final
forces and torques for bodies. We will modify this one later, but for now its implementation
is just good.
But what’s with that NewtonUpdate? This is a function, which does as it says: it
updates NewtonWorld and all its bodies, taking into account the time since the
last update. This function call has one great candidate to be placed into: handleFrame.
But we need to modify that method to receive the time since the last frame been rendered
and we will use this time to update NewtonWorld too.
Remember about architecture: everything, what needs to be exposed to our scripts should be
declared as public in our ScriptManager. Everything else - as protected or private.
This is the basic principle of encapsulation, so let’s keep it in our application.
And update the main application loop:
Hint: to make simulation slower and so watch ball falling in detail, make the
NewtonUpdate argument even smaller. Thousand times, say.
Since we have initialization for our Newton stuff, we need to clean it up at the exit
to prevent memory leaks. Let’s declare a method for that:
And call it right before the program’s end:
And now is the right moment to add key codes’ definitions and exit function to our
ScriptManager so that we could write more clear code and close our application
correctly, using, say, Esc key.
To stop our application, we need to break our while (device->run()) loop. This could be
achieved by simply closing the IrrlichtDevice with device->closeDevice(). But we
do not have an access to the device from the ScriptManager. So let’s add it as a
constructor argument:
So now we can create a function, exposed to our scripts, which will stop our application:
And bind it to the Lua function:
Now we can use our exit function in the Lua scripts. But we will need to use hexadecimal
key codes and that’s… ugly. So we need to define some symbolic names for those codes:
Now we can create a Esc key handler in our script:
Now we are ready to create our first Newton bodies. Bodies are some invisible objects,
which define how our Irrlicht meshes will behave (e. g. where they will be placed,
how they will interact when moving, etc.). Basically, there are two types of bodies:
dynamic, whose movement is determined by the forces, applied to them
kinematic, which are controlled by setting their velocities
Those two kinds of bodies are totally different, so the interactions between them
are not pre-defined. So when your dynamic body will fall onto a kinematic one, it will
fall through.
And each body has its shape, which determines behaviour of the body, when it collides others
and the collision detection itself, of course. Shapes could be convex or concave.
Convex shapes are easier to work with (on the level of physics simulation), but not all the
bodies in practice are convex. For example, levels are oftenly concave. So they need their special
shapes, which are called Triangle Mesh.
Note: to keep the performance of your application high, try to minimalize the use of
triangle meshes and use as simple shapes, as possible. Sometimes it is more effective to
combine a set of primitive shapes, like spheres, cylinders and boxes into one compound
shape, then to use a trimesh.
Let’s create our first simple scene, empowered with physics! We will need only two things:
a sphere
the floor
Since we do not have the good mesh in standard Irrlicht distribution for the floor
(there is a Quake-like level, but that is too much for our case), we will learn how
to make that simple thing in Blender. The next part is a short break between coding
sessions.