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.
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.
Main header file of the irrlicht, the only file needed to include.
define which Quake3 Level should be loaded
#define IRRLICHT_QUAKE3_ARENA
#ifdef ORIGINAL_QUAKE3_ARENA
#define QUAKE3_STORAGE_FORMAT addFolderFileArchive
#define QUAKE3_STORAGE_1 "/baseq3/"
#ifdef CUSTOM_QUAKE3_ARENA
#define QUAKE3_STORAGE_2 "/cf/"
#define QUAKE3_MAP_NAME "maps/cf.bsp"
#else
#define QUAKE3_MAP_NAME "maps/q3dm8.bsp"
#endif
#endif
#ifdef IRRLICHT_QUAKE3_ARENA
#define QUAKE3_STORAGE_FORMAT addFileArchive
#define QUAKE3_STORAGE_1 "../../media/map-20kdm2.pk3"
#define QUAKE3_MAP_NAME "maps/20kdm2.bsp"
#endif
using namespace scene;
Everything in the Irrlicht Engine can be found in this namespace.
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
A class to produce a series of screenshots
{
public:
CScreenShotFactory(
IrrlichtDevice *device,
const c8 * templateName, ISceneNode* node )
: Device(device), Number(0), FilenameTemplate(templateName), Node(node)
{
FilenameTemplate.replace ( '/', '_' );
FilenameTemplate.replace ( '\\', '_' );
}
bool OnEvent(
const SEvent& event)
{
if ((event.
EventType == EET_KEY_INPUT_EVENT) &&
{
{
video::IImage* image = Device->getVideoDriver()->createScreenShot();
if (image)
{
snprintf(buf, 256, "%s_shot%04d.jpg",
FilenameTemplate.c_str(),
++Number);
Device->getVideoDriver()->writeImageToFile(image, buf, 85 );
}
}
else
{
if (Node->isDebugDataVisible())
Node->setDebugDataVisible(scene::EDS_OFF);
else
Node->setDebugDataVisible(scene::EDS_BBOX_ALL);
}
}
return false;
}
private:
ISceneNode* Node;
};
Interface of an object which can receive events.
bool drop() const
Drops the object. Decrements the reference counter by one.
The Irrlicht device. You can create it with createDevice() or createDeviceEx().
Axis aligned bounding box in 3d dimensional space.
Interface for software image data.
unsigned int u32
32 bit unsigned variable.
char c8
8 bit character variable.
SEvents hold information about an event. See irr::IEventReceiver for details on event handling.
struct SKeyInput KeyInput
Ok, lets start.
Like in the HelloWorld example, we create an IrrlichtDevice with createDevice(). The difference now is that we ask the user to select which hardware accelerated driver to use. The Software device would be too slow to draw a huge Quake 3 map, but just for the fun of it, we make this decision possible too.
if (driverType==video::EDT_COUNT)
return 1;
IrrlichtDevice *device = createDevice(driverType, videoDim, 32,
false );
if (device == 0)
return 1;
const char* mapname=0;
if (argc>2)
mapname = argv[2];
else
mapname = QUAKE3_MAP_NAME;
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 write device->getVideoDriver() and device->getSceneManager().
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.
virtual gui::IGUIEnvironment * getGUIEnvironment()=0
Provides access to the 2d user interface environment.
virtual io::IFileSystem * getFileSystem()=0
Provides access to the virtual file system.
GUI Environment. Used as factory and manager of all other GUI elements.
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.
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 other than .zip files. So we add the .pk3 file to our FileSystem. After it was added, we are able to read from the files in that archive as they would directly be stored on disk.
if (argc>2)
else
device->
getFileSystem()->QUAKE3_STORAGE_FORMAT(QUAKE3_STORAGE_1);
#ifdef QUAKE3_STORAGE_2
device->
getFileSystem()->QUAKE3_STORAGE_FORMAT(QUAKE3_STORAGE_2);
#endif
virtual void setAttribute(const c8 *attributeName, s32 value)=0
Sets an attribute as integer value.
virtual io::IAttributes * getParameters()=0
Get interface to the parameters set in this scene.
Now we can load the mesh by calling getMesh(). We get a pointer returned to a IAnimatedMesh. As you know, Quake 3 maps are not really animated, they are only a huge chunk of static geometry with some materials attached. Hence the IAnimated mesh 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 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 AnimatedMeshSceneNode, which would draw always the complete geometry of the mesh, without optimization. Try it out: Write addAnimatedMeshSceneNode instead of addOctreeSceneNode and compare the primitives drawn by the video driver. (There is a getPrimitiveCountDrawed() method in the IVideoDriver class). Note that this optimization with the Octree is only useful when drawing huge meshes consisting of lots of geometry.
Interface for a Mesh which can be loaded directly from a Quake3 .bsp-file.
virtual IAnimatedMesh * getMesh(const io::path &filename)=0
Get pointer to an animateable mesh. Loads the file if not loaded already.
add the geometry mesh to the Scene ( polygon & patches ) The Geometry mesh is optimised for faster drawing
if (mesh)
{
}
CScreenShotFactory screenshotFactory(device, mapname, node);
virtual void setEventReceiver(IEventReceiver *receiver)=0
Sets a new user event receiver which will receive events from the engine.
virtual IMesh * getMesh(s32 frame, s32 detailLevel=255, s32 startFrameLoop=-1, s32 endFrameLoop=-1)=0
Returns the IMesh interface for a frame.
Class which holds the geometry of an object.
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.
now construct SceneNodes for each Shader The Objects are stored in the quake mesh scene::E_Q3_MESH_ITEMS and the Shader ID is stored in the MaterialParameters mostly dark looking skulls and moving lava.. or green flashing tubes?
if ( mesh )
{
#ifdef SHOW_SHADER_NAME
#endif
{
const IMeshBuffer* meshBuffer = additional_mesh->
getMeshBuffer(i);
const quake3::IShader *shader = mesh->
getShader(shaderIndex);
if (0 == shader)
{
continue;
}
#ifdef SHOW_SHADER_NAME
count += 1;
font, name.c_str(), node,
#endif
}
}
virtual IGUIFont * getFont(const io::path &filename)=0
Returns pointer to the font with the specified filename.
virtual u32 getMeshBufferCount() const =0
Get the amount of mesh buffers.
virtual IMeshBuffer * getMeshBuffer(u32 nr) const =0
Get pointer to a mesh buffer.
virtual const quake3::IShader * getShader(const c8 *filename, bool fileNameIsValid=true)=0
loads the shader definition from file
virtual IMeshSceneNode * addQuake3SceneNode(const IMeshBuffer *meshBuffer, const quake3::IShader *shader, ISceneNode *parent=0, s32 id=-1)=0
Adds a quake3 scene node to the scene graph.
virtual IBillboardTextSceneNode * addBillboardTextSceneNode(gui::IGUIFont *font, const wchar_t *text, ISceneNode *parent=0, const core::dimension2d< f32 > &size=core::dimension2d< f32 >(10.0f, 10.0f), const core::vector3df &position=core::vector3df(0, 0, 0), s32 id=-1, video::SColor colorTop=0xFFFFFFFF, video::SColor colorBottom=0xFFFFFFFF)=0
Adds a text scene node, which uses billboards. The node, and the text on it, will scale with distance...
virtual const c8 * getName() const
Returns the name of the node.
Struct for holding parameters for a material renderer.
f32 MaterialTypeParam2
Second free parameter, dependent on the material type.
signed int s32
32 bit signed variable.
Now we only need a Camera to look at the Quake 3 map. And we want to create a user controlled camera. There are some different cameras available in the Irrlicht engine. For example the Maya Camera which can be controlled comparable to 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 addCameraSceneNodeMaya(). But for this example, we want to create a camera which behaves like the ones in first person shooter games (FPS).
Scene Node which is a (controlable) camera.
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...
so we need a good starting Position in the level. we can ask the Quake3 Loader for all entities with class_name "info_player_deathmatch" we choose a random launch
if ( mesh )
{
quake3::IEntity search;
search.name = "info_player_deathmatch";
s32 index = entityList.binary_search(search);
if (index >= 0)
{
do
{
const quake3::SVarGroup *group = entityList[index].getGroup(1);
quake3::getAsVector3df(group->get("origin"), parsepos);
parsepos = 0;
const f32 angle = quake3::getAsFloat(group->get(
"angle"), parsepos);
target.rotateXZBy(angle);
++index;
virtual void setTarget(const core::vector3df &pos)=0
Sets the look at target of the camera.
virtual quake3::tQ3EntityList & getEntityList()=0
get's an interface to the entities
virtual void setPosition(const core::vector3df &newpos)
Sets the position of the node relative to its parent.
float f32
32 bit floating point variable.
notEndList = ( index < (s32) entityList.size () && entityList[index].name == search.name && (device->getTimer()->getRealTime() >> 3 ) & 1 );
notEndList = index == 2;
} while ( notEndList );
}
}
The mouse cursor needs not to be visible, so we make it invisible.
switch ( driverType )
{
case video::EDT_BURNINGSVIDEO:
break;
case video::EDT_OPENGL:
break;
case video::EDT_DIRECT3D8:
case video::EDT_DIRECT3D9:
break;
}
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.
virtual IGUIImage * addImage(video::ITexture *image, core::position2d< s32 > pos, bool useAlphaChannel=true, IGUIElement *parent=0, s32 id=-1, const wchar_t *text=0)=0
Adds an image element.
virtual ITexture * getTexture(const io::path &filename)=0
Get access to a named texture.
We have done everything, so lets draw it. We also write the current frames per second and the drawn primitives to the caption of the window. The 'if (device->isWindowActive())' line is optional, but prevents the engine render to set the position of the mouse cursor after task switching when other program are active.
int lastFPS = -1;
{
{
str += "] FPS:";
str += fps;
#ifdef _IRR_SCENEMANAGER_DEBUG
str += " Cull:";
str += "/";
str += " Draw: ";
str += "/";
str += "/";
#endif
lastFPS = fps;
}
}
virtual bool run()=0
Runs the device.
virtual void setWindowCaption(const wchar_t *text)=0
Sets the caption of the window.
virtual bool isWindowActive() const =0
Returns if the window is active.
virtual void drawAll()=0
Draws all gui elements by traversing the GUI environment starting at the root node.
Provides a generic interface for attributes and their values and the possiblity to serialize them.
virtual s32 getAttributeAsInt(const c8 *attributeName) const =0
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.
In the end, delete the Irrlicht device.