Irrlicht 3D Engine
 
Loading...
Searching...
No Matches
Tutorial 2: Quake3Map

This Tutorial shows how to load a Quake 3 map into the engine, create a SceneNode for optimizing the speed of rendering, and how to create a user controlled camera.

Please note that you should know the basics of the engine before starting this tutorial. Just take a short look at the first tutorial, if you haven't done this yet: http://irrlicht.sourceforge.net/tut001.html

Lets start like the HelloWorld example: We include the irrlicht header files and an additional file to be able to ask the user for a driver type using the console.

#include <irrlicht.h>
#include <iostream>
Main header file of the irrlicht, the only file needed to include.

As already written in the HelloWorld example, in the Irrlicht Engine everything can be found in the namespace 'irr'. To get rid of the irr:: in front of the name of every class, we tell the compiler that we use that namespace from now on, and we will not have to write that 'irr::'. There are 5 other sub namespaces 'core', 'scene', 'video', 'io' and 'gui'. Unlike in the HelloWorld example, we do not call 'using namespace' for these 5 other namespaces, because in this way you will see what can be found in which namespace. But if you like, you can also include the namespaces like in the previous example.

using namespace irr;
Everything in the Irrlicht Engine can be found in this namespace.
Definition aabbox3d.h:13

Again, to be able to use the Irrlicht.DLL file, we need to link with the Irrlicht.lib. We could set this option in the project settings, but to make it easy, we use a pragma comment lib:

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

Ok, lets start. Again, we use the main() method as start, not the WinMain().

int main()
{

Like in the HelloWorld example, we create an IrrlichtDevice with createDevice(). The difference now is that we ask the user to select which video driver to use. The Software device might be too slow to draw a huge Quake 3 map, but just for the fun of it, we make this decision possible, too. Instead of copying this whole code into your app, you can simply include driverChoice.h from Irrlicht's include directory. The function driverChoiceConsole does exactly the same.

// ask user for driver
printf("Please select the driver you want for this example:\n"\
" (a) OpenGL 1.5\n (b) Direct3D 9.0c\n (c) Direct3D 8.1\n"\
" (d) Burning's Software Renderer\n (e) Software Renderer\n"\
" (f) NullDevice\n (otherKey) exit\n\n");
char i;
std::cin >> i;
switch(i)
{
case 'a': driverType = video::EDT_OPENGL; break;
case 'b': driverType = video::EDT_DIRECT3D9;break;
case 'c': driverType = video::EDT_DIRECT3D8;break;
case 'd': driverType = video::EDT_BURNINGSVIDEO;break;
case 'e': driverType = video::EDT_SOFTWARE; break;
case 'f': driverType = video::EDT_NULL; break;
default: return 1;
}
// create device and exit if creation failed
IrrlichtDevice *device =
createDevice(driverType, core::dimension2d<u32>(640, 480));
if (device == 0)
return 1; // could not create selected driver.
The Irrlicht device. You can create it with createDevice() or createDeviceEx().
Axis aligned bounding box in 3d dimensional space.
Definition aabbox3d.h:22
E_DRIVER_TYPE
An enum for all types of drivers the Irrlicht Engine supports.

Get a pointer to the video driver and the SceneManager so that we do not always have to call irr::IrrlichtDevice::getVideoDriver() and irr::IrrlichtDevice::getSceneManager().

video::IVideoDriver* driver = device->getVideoDriver();
virtual scene::ISceneManager * getSceneManager()=0
Provides access to the scene manager.
virtual video::IVideoDriver * getVideoDriver()=0
Provides access to the video driver for drawing 3d and 2d geometry.
The Scene Manager manages scene nodes, mesh recources, cameras and all the other stuff.
Interface to driver which is able to perform 2d and 3d graphics functions.

To display the Quake 3 map, we first need to load it. Quake 3 maps are packed into .pk3 files which are nothing else than .zip files. So we add the .pk3 file to our irr::io::IFileSystem. After it was added, we are able to read from the files in that archive as if they are directly stored on the disk.

device->getFileSystem()->addFileArchive("../../media/map-20kdm2.pk3");
virtual io::IFileSystem * getFileSystem()=0
Provides access to the virtual file system.
virtual bool addFileArchive(const path &filename, bool ignoreCase=true, bool ignorePaths=true, E_FILE_ARCHIVE_TYPE archiveType=EFAT_UNKNOWN, const core::stringc &password="", IFileArchive **retArchive=0)=0
Adds an archive to the file system.

Now we can load the mesh by calling irr::scene::ISceneManager::getMesh(). We get a pointer returned to an irr::scene::IAnimatedMesh. As you might know, Quake 3 maps are not really animated, they are only a huge chunk of static geometry with some materials attached. Hence the IAnimatedMesh consists of only one frame, so we get the "first frame" of the "animation", which is our quake level and create an Octree scene node with it, using irr::scene::ISceneManager::addOctreeSceneNode(). The Octree optimizes the scene a little bit, trying to draw only geometry which is currently visible. An alternative to the Octree would be a irr::scene::IMeshSceneNode, which would always draw the complete geometry of the mesh, without optimization. Try it: Use irr::scene::ISceneManager::addMeshSceneNode() instead of addOctreeSceneNode() and compare the primitives drawn by the video driver. (There is a irr::video::IVideoDriver::getPrimitiveCountDrawn() method in the irr::video::IVideoDriver class). Note that this optimization with the Octree is only useful when drawing huge meshes consisting of lots of geometry.

scene::IAnimatedMesh* mesh = smgr->getMesh("20kdm2.bsp");
scene::ISceneNode* node = 0;
if (mesh)
node = smgr->addOctreeSceneNode(mesh->getMesh(0), 0, -1, 1024);
// node = smgr->addMeshSceneNode(mesh->getMesh(0));
Interface for an animated mesh.
virtual IMesh * getMesh(s32 frame, s32 detailLevel=255, s32 startFrameLoop=-1, s32 endFrameLoop=-1)=0
Returns the IMesh interface for a frame.
virtual IAnimatedMesh * getMesh(const io::path &filename)=0
Get pointer to an animateable mesh. Loads the file if not loaded already.
virtual IMeshSceneNode * addOctreeSceneNode(IAnimatedMesh *mesh, ISceneNode *parent=0, s32 id=-1, s32 minimalPolysPerNode=512, bool alsoAddIfMeshPointerZero=false)=0
Adds a scene node for rendering using a octree to the scene graph.
Scene node interface.
Definition ISceneNode.h:41

Because the level was not modelled around the origin (0,0,0), we translate the whole level a little bit. This is done on irr::scene::ISceneNode level using the methods irr::scene::ISceneNode::setPosition() (in this case), irr::scene::ISceneNode::setRotation(), and irr::scene::ISceneNode::setScale().

if (node)
node->setPosition(core::vector3df(-1300,-144,-1249));
virtual void setPosition(const core::vector3df &newpos)
Sets the position of the node relative to its parent.
Definition ISceneNode.h:507

Now we only need a camera to look at the Quake 3 map. We want to create a user controlled camera. There are some cameras available in the Irrlicht engine. For example the MayaCamera which can be controlled like the camera in Maya: Rotate with left mouse button pressed, Zoom with both buttons pressed, translate with right mouse button pressed. This could be created with irr::scene::ISceneManager::addCameraSceneNodeMaya(). But for this example, we want to create a camera which behaves like the ones in first person shooter games (FPS) and hence use irr::scene::ISceneManager::addCameraSceneNodeFPS().

virtual ICameraSceneNode * addCameraSceneNodeFPS(ISceneNode *parent=0, f32 rotateSpeed=100.0f, f32 moveSpeed=0.5f, s32 id=-1, SKeyMap *keyMapArray=0, s32 keyMapSize=0, bool noVerticalMovement=false, f32 jumpSpeed=0.f, bool invertMouse=false, bool makeActive=true)=0
Adds a camera scene node with an animator which provides mouse and keyboard control appropriate for f...

The mouse cursor needs not be visible, so we hide it via the irr::IrrlichtDevice::ICursorControl.

device->getCursorControl()->setVisible(false);
virtual gui::ICursorControl * getCursorControl()=0
Provides access to the cursor control.
virtual void setVisible(bool visible)=0
Changes the visible state of the mouse cursor.

We have done everything, so lets draw it. We also write the current frames per second and the primitives drawn into the caption of the window. The test for irr::IrrlichtDevice::isWindowActive() is optional, but prevents the engine to grab the mouse cursor after task switching when other programs are active. The call to irr::IrrlichtDevice::yield() will avoid the busy loop to eat up all CPU cycles when the window is not active.

int lastFPS = -1;
while(device->run())
{
if (device->isWindowActive())
{
driver->beginScene(true, true, video::SColor(255,200,200,200));
smgr->drawAll();
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Irrlicht Engine - Quake 3 Map example [";
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
else
device->yield();
}
virtual bool run()=0
Runs the device.
virtual void setWindowCaption(const wchar_t *text)=0
Sets the caption of the window.
virtual void yield()=0
Cause the device to temporarily pause execution and let other processes run.
virtual bool isWindowActive() const =0
Returns if the window is active.
virtual void drawAll()=0
Draws all the scene nodes.
virtual bool beginScene(bool backBuffer=true, bool zBuffer=true, SColor color=SColor(255, 0, 0, 0), const SExposedVideoData &videoData=SExposedVideoData(), core::rect< s32 > *sourceRect=0)=0
Applications must call this method before performing any rendering.
virtual s32 getFPS() const =0
Returns current frames per second value.
virtual const wchar_t * getName() const =0
Gets name of this video driver.
virtual bool endScene()=0
Presents the rendered image to the screen.
Class representing a 32 bit ARGB color.
Definition SColor.h:202

In the end, delete the Irrlicht device.

device->drop();
return 0;
}
bool drop() const
Drops the object. Decrements the reference counter by one.

That's it. Compile and play around with the program.