1
/** Example 016 Quake3 Map Shader Support
3
This Tutorial shows how to load a Quake 3 map into the
4
engine, create a SceneNode for optimizing the speed of
5
rendering and how to create a user controlled camera.
7
Lets start like the HelloWorld example: We include
8
the irrlicht header files and an additional file to be able
9
to ask the user for a driver type using the console.
12
#include "driverChoice.h"
15
define which Quake3 Level should be loaded
17
#define IRRLICHT_QUAKE3_ARENA
18
//#define ORIGINAL_QUAKE3_ARENA
19
//#define CUSTOM_QUAKE3_ARENA
20
//#define SHOW_SHADER_NAME
22
#ifdef ORIGINAL_QUAKE3_ARENA
23
#define QUAKE3_STORAGE_FORMAT addFolderFileArchive
24
#define QUAKE3_STORAGE_1 "/baseq3/"
25
#ifdef CUSTOM_QUAKE3_ARENA
26
#define QUAKE3_STORAGE_2 "/cf/"
27
#define QUAKE3_MAP_NAME "maps/cf.bsp"
29
#define QUAKE3_MAP_NAME "maps/q3dm8.bsp"
33
#ifdef IRRLICHT_QUAKE3_ARENA
34
#define QUAKE3_STORAGE_FORMAT addFileArchive
35
#define QUAKE3_STORAGE_1 "../../media/map-20kdm2.pk3"
36
#define QUAKE3_MAP_NAME "maps/20kdm2.bsp"
40
using namespace scene;
43
Again, to be able to use the Irrlicht.DLL file, we need to link with the
44
Irrlicht.lib. We could set this option in the project settings, but
45
to make it easy, we use a pragma comment lib:
48
#pragma comment(lib, "Irrlicht.lib")
53
A class to produce a series of screenshots
55
class CScreenShotFactory : public IEventReceiver
59
CScreenShotFactory( IrrlichtDevice *device, const c8 * templateName, ISceneNode* node )
60
: Device(device), Number(0), FilenameTemplate(templateName), Node(node)
62
FilenameTemplate.replace ( '/', '_' );
63
FilenameTemplate.replace ( '\\', '_' );
66
bool OnEvent(const SEvent& event)
68
// check if user presses the key F9
69
if ((event.EventType == EET_KEY_INPUT_EVENT) &&
70
event.KeyInput.PressedDown)
72
if (event.KeyInput.Key == KEY_F9)
74
video::IImage* image = Device->getVideoDriver()->createScreenShot();
78
snprintf(buf, 256, "%s_shot%04d.jpg",
79
FilenameTemplate.c_str(),
81
Device->getVideoDriver()->writeImageToFile(image, buf, 85 );
86
if (event.KeyInput.Key == KEY_F8)
88
if (Node->isDebugDataVisible())
89
Node->setDebugDataVisible(scene::EDS_OFF);
91
Node->setDebugDataVisible(scene::EDS_BBOX_ALL);
98
IrrlichtDevice *Device;
100
core::stringc FilenameTemplate;
109
int IRRCALLCONV main(int argc, char* argv[])
112
Like in the HelloWorld example, we create an IrrlichtDevice with
113
createDevice(). The difference now is that we ask the user to select
114
which hardware accelerated driver to use. The Software device would be
115
too slow to draw a huge Quake 3 map, but just for the fun of it, we make
116
this decision possible too.
119
// ask user for driver
120
video::E_DRIVER_TYPE driverType=driverChoiceConsole();
121
if (driverType==video::EDT_COUNT)
124
// create device and exit if creation failed
125
const core::dimension2du videoDim(800,600);
127
IrrlichtDevice *device = createDevice(driverType, videoDim, 32, false );
130
return 1; // could not create selected driver.
132
const char* mapname=0;
136
mapname = QUAKE3_MAP_NAME;
139
Get a pointer to the video driver and the SceneManager so that
140
we do not always have to write device->getVideoDriver() and
141
device->getSceneManager().
143
video::IVideoDriver* driver = device->getVideoDriver();
144
scene::ISceneManager* smgr = device->getSceneManager();
145
gui::IGUIEnvironment* gui = device->getGUIEnvironment();
147
//! add our private media directory to the file system
148
device->getFileSystem()->addFileArchive("../../media/");
151
To display the Quake 3 map, we first need to load it. Quake 3 maps
152
are packed into .pk3 files, which are nothing other than .zip files.
153
So we add the .pk3 file to our FileSystem. After it was added,
154
we are able to read from the files in that archive as they would
155
directly be stored on disk.
158
device->getFileSystem()->QUAKE3_STORAGE_FORMAT(argv[1]);
160
device->getFileSystem()->QUAKE3_STORAGE_FORMAT(QUAKE3_STORAGE_1);
161
#ifdef QUAKE3_STORAGE_2
162
device->getFileSystem()->QUAKE3_STORAGE_FORMAT(QUAKE3_STORAGE_2);
165
// Quake3 Shader controls Z-Writing
166
smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);
169
Now we can load the mesh by calling getMesh(). We get a pointer returned
170
to a IAnimatedMesh. As you know, Quake 3 maps are not really animated,
171
they are only a huge chunk of static geometry with some materials
172
attached. Hence the IAnimated mesh consists of only one frame,
173
so we get the "first frame" of the "animation", which is our quake level
174
and create an Octree scene node with it, using addOctreeSceneNode().
175
The Octree optimizes the scene a little bit, trying to draw only geometry
176
which is currently visible. An alternative to the Octree would be a
177
AnimatedMeshSceneNode, which would draw always the complete geometry of
178
the mesh, without optimization. Try it out: Write addAnimatedMeshSceneNode
179
instead of addOctreeSceneNode and compare the primitives drawed by the
180
video driver. (There is a getPrimitiveCountDrawed() method in the
181
IVideoDriver class). Note that this optimization with the Octree is only
182
useful when drawing huge meshes consisting of lots of geometry.
184
scene::IQ3LevelMesh* const mesh =
185
(scene::IQ3LevelMesh*) smgr->getMesh(mapname);
188
add the geometry mesh to the Scene ( polygon & patches )
189
The Geometry mesh is optimised for faster drawing
191
scene::ISceneNode* node = 0;
194
scene::IMesh * const geometry = mesh->getMesh(quake3::E_Q3_MESH_GEOMETRY);
195
node = smgr->addOctreeSceneNode(geometry, 0, -1, 4096);
198
// create an event receiver for making screenshots
199
CScreenShotFactory screenshotFactory(device, mapname, node);
200
device->setEventReceiver(&screenshotFactory);
203
now construct SceneNodes for each Shader
204
The Objects are stored in the quake mesh scene::E_Q3_MESH_ITEMS
205
and the Shader ID is stored in the MaterialParameters
206
mostly dark looking skulls and moving lava.. or green flashing tubes?
210
// the additional mesh can be quite huge and is unoptimized
211
const scene::IMesh * const additional_mesh = mesh->getMesh(quake3::E_Q3_MESH_ITEMS);
213
#ifdef SHOW_SHADER_NAME
214
gui::IGUIFont *font = device->getGUIEnvironment()->getFont("../../media/fontlucida.png");
218
for ( u32 i = 0; i!= additional_mesh->getMeshBufferCount(); ++i )
220
const IMeshBuffer* meshBuffer = additional_mesh->getMeshBuffer(i);
221
const video::SMaterial& material = meshBuffer->getMaterial();
223
// The ShaderIndex is stored in the material parameter
224
const s32 shaderIndex = (s32) material.MaterialTypeParam2;
226
// the meshbuffer can be rendered without additional support, or it has no shader
227
const quake3::IShader *shader = mesh->getShader(shaderIndex);
233
// we can dump the shader to the console in its
234
// original but already parsed layout in a pretty
235
// printers way.. commented out, because the console
237
// quake3::dumpShader ( Shader );
239
node = smgr->addQuake3SceneNode(meshBuffer, shader);
241
#ifdef SHOW_SHADER_NAME
243
core::stringw name( node->getName() );
244
node = smgr->addBillboardTextSceneNode(
245
font, name.c_str(), node,
246
core::dimension2d<f32>(80.0f, 8.0f),
247
core::vector3df(0, 10, 0));
253
Now we only need a Camera to look at the Quake 3 map. And we want to
254
create a user controlled camera. There are some different cameras
255
available in the Irrlicht engine. For example the Maya Camera which can
256
be controlled compareable to the camera in Maya: Rotate with left mouse
257
button pressed, Zoom with both buttons pressed, translate with right
258
mouse button pressed. This could be created with
259
addCameraSceneNodeMaya(). But for this example, we want to create a
260
camera which behaves like the ones in first person shooter games (FPS).
263
scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
266
so we need a good starting Position in the level.
267
we can ask the Quake3 Loader for all entities with class_name
268
"info_player_deathmatch"
269
we choose a random launch
273
quake3::tQ3EntityList &entityList = mesh->getEntityList();
275
quake3::IEntity search;
276
search.name = "info_player_deathmatch";
278
s32 index = entityList.binary_search(search);
284
const quake3::SVarGroup *group = entityList[index].getGroup(1);
287
const core::vector3df pos =
288
quake3::getAsVector3df(group->get("origin"), parsepos);
291
const f32 angle = quake3::getAsFloat(group->get("angle"), parsepos);
293
core::vector3df target(0.f, 0.f, 1.f);
294
target.rotateXZBy(angle);
296
camera->setPosition(pos);
297
camera->setTarget(pos + target);
301
notEndList = ( index < (s32) entityList.size () &&
302
entityList[index].name == search.name &&
303
(device->getTimer()->getRealTime() >> 3 ) & 1
306
notEndList = index == 2;
307
} while ( notEndList );
312
The mouse cursor needs not to be visible, so we make it invisible.
315
device->getCursorControl()->setVisible(false);
317
// load the engine logo
318
gui->addImage(driver->getTexture("irrlichtlogo2.png"),
319
core::position2d<s32>(10, 10));
321
// show the driver logo
322
const core::position2di pos(videoDim.Width - 128, videoDim.Height - 64);
324
switch ( driverType )
326
case video::EDT_BURNINGSVIDEO:
327
gui->addImage(driver->getTexture("burninglogo.png"), pos);
329
case video::EDT_OPENGL:
330
gui->addImage(driver->getTexture("opengllogo.png"), pos);
332
case video::EDT_DIRECT3D8:
333
case video::EDT_DIRECT3D9:
334
gui->addImage(driver->getTexture("directxlogo.png"), pos);
339
We have done everything, so lets draw it. We also write the current
340
frames per second and the drawn primitives to the caption of the
341
window. The 'if (device->isWindowActive())' line is optional, but
342
prevents the engine render to set the position of the mouse cursor
343
after task switching when other program are active.
348
if (device->isWindowActive())
350
driver->beginScene(true, true, video::SColor(255,20,20,40));
355
int fps = driver->getFPS();
356
//if (lastFPS != fps)
358
io::IAttributes * const attr = smgr->getParameters();
359
core::stringw str = L"Q3 [";
360
str += driver->getName();
364
str += attr->getAttributeAsInt("calls");
366
str += attr->getAttributeAsInt("culled");
368
str += attr->getAttributeAsInt("drawn_solid");
370
str += attr->getAttributeAsInt("drawn_transparent");
372
str += attr->getAttributeAsInt("drawn_transparent_effect");
374
device->setWindowCaption(str.c_str());
380
In the end, delete the Irrlicht device.