Cross-Platform Game Development for C++ Developers, Part III: The 3D Irrlicht Engine

The Irrlicht Engine is a high-performance, real-time 3D engine written and usable in C++ and also available for .NET languages. It is completely cross-platform, using your choice of Direct3D on Windows, OpenGL 1.2, or its own software renderer. Although open source, the Irrlicht library offers state-of-the-art features normally found in commercial 3D engines, such as dynamic shadows, particle systems, character animation, indoor and outdoor technology, and collision detection (see Figure 1).

Figure 1. The Irrlicht 3D Engine

Irrlicht is the name of a German fairy-tale creature that glows and flies, and can be found mostly in the vicinity of swamps. The word "Irrlicht" is the combination of the two German words: "irr," meaning mad; and "Licht," meaning "light." In English, it translates to "will-o'-the-wisp."

Irrlicht is also blessed with a huge, active development community with lots of projects. Yet, because Irrlicht is primarily the work of gaming auteur Nikolaus Gebhardt, it reflects a very cohesive design. You can find enhancements for Irrlicht all over the Web, such as alternative terrain renderers, portal renderers, exporters, world layers, tutorials, and editors. Also, it has independently created language bindings for Java, Perl, Ruby, BASIC, Python, LUA, and more. And best of all: It's completely free.

Inside the Irrlicht Feature Set

Before drilling down into the API, let me get a bit more specific about what Irrlicht has to offer the 3D game developer:

  • An engine that runs on Linux as well as Windows 98, ME, NT, 2000, and XP (MacOS planned)
  • Anti-aliasing support in Direct3D version 8 or 9 renderers (optional)
  • Skinnable-GUI environment (including a cool metallic shaded skin) to give some pizzazz to old dialogs
  • Scene management system that allows seamless indoor and outdoor transitions
  • Character animation system with skeletal and morph target animation
  • A special effects system, including particle effects (rain, smoke, fire, snow, and so forth), billboards, light maps, environment mapping, stencil buffer shadows, fog, texture animation, parallax mapping, bump mapping, and more
  • Built-in materials, including support for Pixel and Vertex Shaders 1.1 to 3.0, ARB Fragment and Vertex Programs, and HLSL (GLSL is planned)
  • .NET language binding, which makes the engine available to all .NET languages such as C#, Visual Basic.NET, and Delphi.NET
  • A built-in platform-independent software renderer, featuring z-buffer, Gouraud shading, alpha-blending and transparency, and fast 2D drawing (see Figure 2)
  • 2D drawing functions you've come to expect, such as alpha blending, color key-based blitting, font drawing, and mixing 3D with 2D graphics
  • Direct import of common mesh file formats: Maya, 3DStudio Max, COLLADA, DeleD, Milkshape, Quake 3 levels, Quake2 models, DirectX, Pulsar, My3DTools, FSRad, and Cartography Shop
  • Direct import of Textures from BMP, PNG, Photoshop, JPEG, Targa, and PCX
  • Fast and easy collision detection and response
  • Optimized fast 3D math and container template libraries
  • Directly reading from (compressed) archives (.zip)
  • Integrated fast XML parser
  • Unicode support for easy localization

Figure 2: Irrlicht-based game called Yet Another Space Shooter (YASS) displays breathtakingly good rendering for a static game frame

Special Effects in Irrlicht

I always like to show off just a little bit more than "hello world" when I'm demo-ing a new game engine. In this example, I'll show how to use stencil buffer shadows, the particle system, billboards, dynamic light, and the water surface scene node—all at the same time (see Figure 3).

Figure 3. Render This Scene with Dynamic Light and Water

The Irrlicht Engine automatically checks to see whether your hardware supports the stencil buffer, and disables shadows if it's not present. In this demo, the 'shadows' flag in createDevice() is set to render a dynamic shadow cast from an animated character. If this example runs too slowly on your PC, set it to false or buy a better graphics card.

To be able to use the Irrlicht.DLL file, you need to link with the Irrlicht.lib. You could set this option in the project settings, but to make it easy, use a pragma comment. The createDevice() instantiates the root object for doing everything with the engine. Parameters are as follows:

  • deviceType: The device type. This can currently be the Null device, the Software device, DirectX8, DirectX9, or OpenGL.
  • windowSize: Size of the window or full screen mode to be created. This example uses 512x384.
  • bits: Number of bits per pixel (when in full screen mode). The only values are 16 or 32.
  • fullscreen: Specifies whether you want the device to run in full screen mode or not.
  • stencilbuffer: Specifies whether you want to use the stencil buffer for drawing shadows.
  • vsync: Specifies whether you want to have vsync enabled (full screen only). Optional
  • eventReceiver: An object to receive events. Optional

For the example environment, you'll load a 3D Studio Max file of a room. The room is nothing much to look at, but the Irrlicht Engine is able to create a cool texture mapping for you. Just use the mesh manipulator and create a planar texture mapping for the mesh:

#include <irrlicht.h>
#include <iostream>

using namespace irr;

#pragma comment(lib, "Irrlicht.lib")

int main()
{
   // Let's assume the user wants OpenGL for this example
   // could have specified DirectX 8, 9, etc.
   video::E_DRIVER_TYPE driverType = video::EDT_OPENGL;

   // create device and exit if creation failed
   IrrlichtDevice *device = createDevice(driverType,
      core::dimension2d<s32>(640, 480), 16, false, true);

   if (device == 0)
      return 1;

video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();

I wasn't happy with the emissive light color from this import file. The following shows how to implement these steps:

scene::IAnimatedMesh* mesh = smgr->getMesh("room.3ds");

smgr->getMeshManipulator()->makePlanarTextureMapping(
   mesh->getMesh(0), 0.008f);

scene::ISceneNode* node = 0;

node = smgr->addAnimatedMeshSceneNode(mesh);
node->setMaterialTexture(0, driver->getTexture("wall.jpg"));
node->getMaterial(0).EmissiveColor.set(0,0,0,0);

Water Animation

The first special effect you will add is water animation. To do this, the WaterSurfaceSceneNode takes a mesh as input and makes it wave like a water surface. If you let this scene node use a nice material like the MT_REFLECTION_2_LAYER, it looks really cool:

mesh = smgr->addHillPlaneMesh("myHill",
   core::dimension2d<f32>(20,20),
   core::dimension2d<s32>(40,40), 0, 0,
   core::dimension2d<f32>(0,0),
   core::dimension2d<f32>(10,10));

node = smgr->addWaterSurfaceSceneNode(mesh->getMesh(0),3,300,30);
node->setPosition(core::vector3df(0,7,0));

node->setMaterialTexture(0,driver->getTexture("water.jpg"));
node->setMaterialTexture(1,driver->getTexture("stones.jpg"));

node->setMaterialType(video::EMT_REFLECTION_2_LAYER);

As input mesh, you create a hilly plane mesh, but you could use any other mesh for this. You could even re-use the room.3ds input file (which would look really strange). The example also paints all the other surfaces with a generic stone texture pattern.

Transparent Billboard and Light

The second special effect is very basic but highly useful: a transparent billboard combined with a dynamic light. To make this happen, you simply create a light scene node, let it fly around, and, to make it look cooler, attach a billboard scene node to it:

// create light

node = smgr->addLightSceneNode(0, core::vector3df(0,0,0),
             video::SColorf(1.0f, 0.6f, 0.7f, 1.0f), 600.0f);
scene::ISceneNodeAnimator* anim = 0;
anim = smgr->createFlyCircleAnimator(core::vector3df(0,150,0),
                                         250.0f);
node->addAnimator(anim);
anim->drop();

// attach billboard to light

node = smgr->addBillboardSceneNode(node, core::dimension2d<f32>
                                  (50, 50));
node->setMaterialFlag(video::EMF_LIGHTING, false);
node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
node->setMaterialTexture(0,driver->getTexture("particlewhite.bmp"));

Cross-Platform Game Development for C++ Developers, Part III: The 3D Irrlicht Engine

Particle System

The next special effect is a lot more interesting: a particle system. The particle system in the Irrlicht Engine is both modular and extensible, yet still easy to use. You simply put the particle emitters into a particle system scene node and thus the particles seem to appear from nothing. These emitters are quite configurable and usually have lots of parameters like particle direction, amount, and color.

Of course, emitter types differ (for example, a point emitter that lets particles pop out at a fixed point). If the particle emitters available in the engine are not enough for you, you can easily create your own. Simply derive a new class from the IParticleEmitter interface and attach it to the particle system using setEmitter().

The next example creates a box particle emitter. As you might expect, it creates particles randomly inside a bounding box. The parameters define the box, direction of the particles, minimal and maximal new particles per second, color, and minimal and maximal lifetime of the particles.

A particle system composed entirely of emitters would be both boring and lacking in realism. Therefore, Irrlicht supports particle affectors, which modify particles as they fly around. Once added to the particle system, they can simulate additional effects like gravity or wind. The particle affector in this example simply modifies the color of the particles to produce a fade-out effect.

As you might have guessed, particle affectors are implemented by deriving from IParticleAffector and adding them with addAffector(). After you set a nice material to the particle system, you have a cool-looking campfire. By adjusting material, texture, and particle emitter and affector parameters, you also can easily create smoke, rain, explosions, snow, and so on:

scene::IParticleSystemSceneNode* ps = 0;
ps = smgr->addParticleSystemSceneNode(false);
ps->setPosition(core::vector3df(-70,60,40));
ps->setScale(core::vector3df(2,2,2));

ps->setParticleSize(core::dimension2d<f32>(20.0f, 10.0f));

scene::IParticleEmitter* em = ps->createBoxEmitter(
   core::aabbox3d<f32>(-7,0,-7,7,1,7),
   core::vector3df(0.0f,0.03f,0.0f),
   80,100,
   video::SColor(0,255,255,255), video::SColor(0,255,255,255),
   800,2000);

ps->setEmitter(em);
em->drop();

scene::IParticleAffector* paf =
   ps->createFadeOutParticleAffector();

ps->addAffector(paf);
paf->drop();

ps->setMaterialFlag(video::EMF_LIGHTING, false);
ps->setMaterialTexture(0, driver->getTexture,"particle.bmp"));
ps->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA);

Shadow Cast

Last but not least, you want a dynamic shadow cast from an animated character. For this, you load a Quake2 .md2 model file and place it into your world. To create the shadow, you just call addShadowVolumeSceneNode(). You control the color of shadows, which is adjustable only globally and affects all shadows, by calling ISceneManager::setShadowColor(). Voilà! Here is your dynamic shadow:

mesh = smgr->getMesh("../../media/faerie.md2");
scene::IAnimatedMeshSceneNode* anode = 0;

anode = smgr->addAnimatedMeshSceneNode(mesh);
anode->setPosition(core::vector3df(-50,45,-60));
anode->setMD2Animation(scene::EMAT_STAND);
anode->setMaterialTexture(0, driver->getTexture
                         ("../../media/Faerie5.BMP"));

anode->addShadowVolumeSceneNode();
smgr->setShadowColor(video::SColor(220,0,0,0));

Game Loop

Finally, you can enter the game loop controlled by the device->run() method. The loop will run until the device quits by getting a close window event (such as the ALT-F4 keystroke in Windows). Everything must be drawn between a beginScene() and an endScene() call. The beginScene() clears the screen with a color and also the depth buffer, if desired. Then you let the Scene Manager and the GUI environment draw their content. With the endScene() call, everything is presented on the screen. In this example, you also dynamically display the frames per second (FPS) figure, which is all-important to the serious gamer, on the titlebar:

scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
camera->setPosition(core::vector3df(-50,50,-150));


int lastFPS = -1;

while(device->run())
{
   driver->beginScene(true, true, 0);
   smgr->drawAll();
   driver->endScene();

   int fps = driver->getFPS();
   if (lastFPS != fps)
   {
      core::stringw str = L"Campfire FX example [";
      str += driver->getName();
      str += "] FPS:";
      str += fps;

      device->setWindowCaption(str.c_str());
      lastFPS = fps;
   }
}

device->drop();

After you have finished, you must delete the Irrlicht Device created earlier with createDevice(). With the Irrlicht Engine, you should delete all objects you created with a method or function that starts with 'create'. The object is deleted simply by calling device->drop().

Irrlicht Add-ons You Might Like

As mentioned in the introduction, Irrlicht has a thriving community of independent developers producing add-ons and games. Many improvements by these people are re-integrated into a subsequent Irrlicht release. Here's a brief list of a few I thought would interest prospective developers:

  • OCTTools is a set of tools for Irrlicht created by Murphy McCauley for working with OCT-files: exporters, loaders, and much more.
  • ICE (Irrlicht Common Engine) Framework provides a skeleton project enhancing rapid production of new projects.
  • MIM by Murphy McCauley is a very useful XML-based file format for Irrlicht with loaders, converters, tools, and so on.
  • My3D is a toolkit enabling you to export light-mapped scenes from various 3D packages (3DStudio MAX, Giles, and the like) directly into Irrlicht.
  • Dusty Engine allows programmers to create "tasks" that do anything the programmer wants them to do. The tasks are then added to a general tree of tasks, where each task can have as many children tasks as the designer desires. The "grouping" of tasks allows the game designer to perform common operations such as pause, resume, or destroy on an entire tree.
  • Irrlicht RPG (Erring Light) is a 3D walk-around game engine being developed with RPG-style play in mind.
  • 2D Image and Sprite classes comprise a useful library that extends Irrlicht's 2D capabilities.
  • Zenprogramming is home to the first unofficial external terrain renderer for Irrlicht and a place for tutorials.

Stay Tuned!

Be sure to come back next week for Cross-Platform Game Development for C++ Developers, Part IV.

About the Author

Victor Volkman has been writing for C/C++ Users Journal and other programming journals since the late 1980s. He is a graduate of Michigan Tech and a faculty advisor board member for Washtenaw Community College CIS department. Volkman is the editor of numerous books, including C/C++ Treasure Chest and is the owner of Loving Healing Press. He can help you in your quest for open source tools and libraries, just drop an e-mail to sysop@HAL9K.com.



About the Author

Victor Volkman

Victor Volkman has been writing for C/C++ Users Journal and other programming journals since the late 1980s. He is a graduate of Michigan Tech and a faculty advisor board member for Washtenaw Community College CIS department. Volkman is the editor of numerous books, including C/C++ Treasure Chest and is the owner of Loving Healing Press. He can help you in your quest for open source tools and libraries, just drop an e-mail to sysop@HAL9K.com.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds