I am a college student, working diligently on this project in his free time. If DustyEngine has helped you in any way, please donate just $1!
This tutorial is meant to show you how to build upon a task that has already been defined by the engine. This tutorial assumes you have a basic understanding of the Irrlicht 3d engine. Enough understanding to create a window, read in a mesh, and add mesh nodes and light nodes to the scene. The irrlicht code will be briefly explained but not in detail.
I won't lie to you, this is extremely looooong tutorial :) I tend to draw out my explanations. There really isn't that much code to this, and it's all very simple if you understand basic Irrlicht uses. Dusty Engine greatly simplifies code, and I believe this tutorial shows that.
Sure, the effect that this tutorial achieves could be done without Dusty Engine. But this is a simple example. Dusty Engine's power lies in its scalability, it could handle the tasks in this program and a thousand other custom tasks without problem.
In this tutorial, we are going to modify Dusty Engine's built-in MoveTask. Note that in order to use the engine, you do not have to modify an existing task. You can create new ones that do just about anything. But in this case we will be working with one that already exists.
To see the outcome of the program we are going to write, download Demo 2 above. We are going to create a scene that contains a bunch of little spheres that bounce around the screen. Those spheres are lit by 5 different lights, and each time the program runs the lights are in different random positions and the spheres have different random movement directions. The lights also change to random colors each time the program is executed.
Alright, all that said, lets get into some code. We'll begin with the code of the new task we are creating. I have called this task "BounceMoveTask" because it builds upon the DustyEngine::MoveTask task. The BounceMoveTask adds in a rect which the node position from the MoveTask is tested against. The node is made to "bounce" when it reaches the boundaries of that rect.
I have put this task into a header file
"bouncingmovetask.h".
|
The code above begins by using defines to make sure
this file is only included once. It then includes the dustyengine
include file and irrlicht include file. Straightforward stuff.
class BouncingMoveTask : public DustyEngine::MoveTask |
Above is the definition of the BouncingMoveTask task
and the constructor and destructor. They do nothing except that the
constructor makes sure the MoveTask constructor is called.
void SetWidth(irr::f32 w) |
The bouncing movement task has 2 values of its own
that it needs: the width of the object associated with it (the node
that MoveTask handles) and a bounding rectangle. Each time the object
moves, its position and width are tested against the bounding
rectangle, and if the object has hit the edge of the rectangle, it will
bounce off.
It was also possible to use the bounding rect that is
received from the node through Irrlicht's node's function to get its
bounding rect, but this technique is much simpler for this demo.
void Reset() |
The Reset() function did not have to be implemented for
this task, because I am not using a TaskManager in the main program,
and the task really never has to be reset because it runs until the
user closes the program. But I still implemented it to show the purpose
of the Reset() function. This function returns the task to the state it
was at the time it was instanciated. Namely, it resets the width and
the bounding box to default values. Also, if you are inheriting from
another task, make sure to call the parent class's Reset() function as
well.
void OnUpdate() |
Ahh, now here's the bread and butter of the task. The OnUpdate() function. OnUpdate() is what is called by the TaskTree when the task needs to be executed.
This task's OnUpdate will first move the node that's associated with it, by calling the MoveTask's OnUpdate function. It then moves on to check the position the node is currently at against the edges of the bounding box. OffSet is the width that has been associated with this node divided by 2. This makes sure that the very edge of the node is what gets tested against the edge. If a collision is detected on the X axis, then the moveVector (from MoveTask) has its X direction negated so it'll move in the opposite direction. The same is then done for the Y direction.
This has the desired effect of "bouncing" the object
against the bounding rect.
private: |
And those private variables and the endif are the entirity of the BounceMoveTask task definition. That is how easy it is to create a task in DustyEngine. You can add new functions to customize their use, and then simply implement the abstract functions (we didn't have to do OnPause/Unpause/creation/destruction for this task because they were implemented in MoveTask.)
Now to move on to the main.cpp file.
#include <dustyengine.h> |
This is just the basic includes and namespaces. The
namespace used in Dusty Engine is DustyEngine. Pretty simple :)
vector3df RandomVector(RandGen * randGen, float minx, float maxx, |
This is a helper function which simply uses Dusty
Engine's RandGen class to generate a random Irrlicht 3d vector (a
vector3df) RandGen is not some special super random number generator,
it simply wraps rand() into an easy-to-use class of functions.
SColor RandomColor(RandGen * randGen, int minA, int maxA, |
This is another heper function for this program. This
function will return a random color using Dusty Engine's RandGen class.
The color has random values within a specified range.
void SetupCamera(IrrlichtDevice * irrDevice) |
Now we are getting into the meat and potatoes of this program. This is the function which will create a camera for the scene. The only confusing part of this code is that it creates an orthagonal viewing matrix for the camera. This means that the objects on the scene will not be subject to depth. An object 1000 meters away appears the same as an object 1 meter away.
Also, an orthagonal view constrains the world into a certain range. Using this code, the world in the scene will have the following ranges: x = [-8, 8] and y = [-8, 8]. This is because the total width is 16 and the total height is 16, with the point (0, 0) in the center of the screen.
You should read the tutorial on orthagonal projection
using Irrlicht on Irrlicht's tutorial page, by Saigumi. Click here to view
the tutorial page.
|
Here is a function which will add a certain number of
lights to the scene. Those lights have a random position (within the
range of the world defined in the orthagonal matrix above.) They also
have a random color. Since the random number generator is seeded with
the time in the main function a little below, the colors and locations
of the lights will be new each time the program is run.
|
This is the most important function in the program for Dusty Engine. This is the program which creates the little balls that float around the screen, and creates the task that controls them. For each ball, there is one task. That task will move the ball around the screen, and "bounce" it when it reaches the bounding box.
The first thing this function does is create a node on the scene, using the irrlicht device passed to it, created from the mesh that is passed to it. It then scales that node down, just for display purposes. The scale could be anything, I just tweaked it down to what I felt looked the best.
Next, a bouncing move task is created. After the task is added to the tree, it is forgotten about. The task tree cleans up after itself. When the tree is destroyed, it goes through all the tasks it has and drop()s them. You don't have to worry about manually dropping all your tasks. However, if you're wanting to use the task later one, you need to keep a pointer to it, and grab() it. This way, if the task is ever dropped by the task tree, it won't be deleted out of memory until you're ready to delete it.
After the bouncing move task is created, the program first assignes the bounding box to it. This bounding box matches the dimensions that were supplied earlier when we set up the camera and the orthagonal matrix. This is so the balls will bounce through the whole screen, without going off the screen.
Then the new task is told what node to use, which would be the node that had just been created. The SetNode() function came from MoveTask, so we did not have to define it when we defined the BouncingMoveTask because it was inherited.
A random vector is then given to the task to move on. Once again, this was handled in the MoveTask.
The SetWidth() function follows. To simplify this example, I did not test the bounding box of the node against the bounding box given to the task. I just gave each task a "width" with which to test against the bounding box. I tweaked this a few times to get a number I felt was good, and for the sphere mesh I used I decided 1.5 was the best number.
The most important step follows. The task is then added
to the task tree, under the root node, with a priority of 50. This
means that, system willing, this task will be executed every 50
milliseconds.
|
AddSpheres just simplifies the code a bit, because
we're going to be adding multiple spheres to the scene. It just adds a
certain number of spheres to the scene. I could have done all this in
the main(), but I decided to do this to keep the main as short and
simple as possible.
|
And there's the most important function, the main(). The main function begins by creating an irrlicht device, and creating a dusty driver object using that IrrlichtDevice. DustyDriver is a very important object. When the driver is instanciated, it creates a TaskTree, a TimeServer, and a RandGen object. Those are the main three objects in Dusty Engine.
* The TaskTree handles execution and
pausing/unpausing/execution of tasks.
* The TimeServer handles creation of TimeServerEntry objects, which are
used to manage time in DustyEngine.
* The RandGen wraps around rand() to create an easy-to-use random
number generator.
After the DustyDriver is created, it gets several pointers to important irrlicht and dusty objects, then moves on. The code creates 5 lights using the AddLights function above. It then loads in the sphere.obj mesh. That mesh is sent to the AddSpheres function to create 20 spheres using that mesh.
Then the main Irrilcht loop is begun. Notice that there is another condition in the Irrlicht loop (there is both irrDevice->run() and taskTree->isRunning().) This is because the task tree can be told to stop execution, and the program may want to exit when the task tree is stopped. This allows any task in the program to stop execution of the program. It does not HAVE to do so, in fact this program does not make use of that. But it is an important option, and there for flexibility.
After that a string is created to be the caption of the program showing the FPS and other important info.
The scene is then drawn using the typical Irrilcht beginScene()....drawAll()....endScene() method.
What follows that is extremely important. The task tree is told to execute all nodes that need to be executed. If that line were not executed, the spheres would just sit there and never move. This is when the BouncingMoveTask of each node is actually executed, if enough time has passed.
After the loop, the DustyDriver is dropped, the IrrDevice is dropped, and the program is exited.
That is a very simple program showing the use of Dusty Engine. This has been an extremely long tutorial, but was a joy to write. I hope I have answered any questions, and shown someone the power of this engine. I truly belive this is a very powerful tool for programmers and I plan to use it in all my Irrlicht projects.
-Dave Andrews