~baltix/+junk/irrlicht-test

« back to all changes in this revision

Viewing changes to examples/16.Quake3MapShader/main.cpp

  • Committer: Mantas Kriaučiūnas
  • Date: 2011-07-18 13:06:25 UTC
  • Revision ID: mantas@akl.lt-20110718130625-c5pvifp61e7kj1ol
Included whole irrlicht SVN libraries to work around launchpad recipe issue with quilt, see https://answers.launchpad.net/launchpad/+question/165193

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** Example 016 Quake3 Map Shader Support
 
2
 
 
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.
 
6
 
 
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.
 
10
*/
 
11
#include <irrlicht.h>
 
12
#include "driverChoice.h"
 
13
 
 
14
/*
 
15
        define which Quake3 Level should be loaded
 
16
*/
 
17
#define IRRLICHT_QUAKE3_ARENA
 
18
//#define ORIGINAL_QUAKE3_ARENA
 
19
//#define CUSTOM_QUAKE3_ARENA
 
20
//#define SHOW_SHADER_NAME
 
21
 
 
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"
 
28
        #else
 
29
                #define QUAKE3_MAP_NAME                 "maps/q3dm8.bsp"
 
30
        #endif
 
31
#endif
 
32
 
 
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"
 
37
#endif
 
38
 
 
39
using namespace irr;
 
40
using namespace scene;
 
41
 
 
42
/*
 
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:
 
46
*/
 
47
#ifdef _MSC_VER
 
48
#pragma comment(lib, "Irrlicht.lib")
 
49
#endif
 
50
 
 
51
 
 
52
/*
 
53
A class to produce a series of screenshots
 
54
*/
 
55
class CScreenShotFactory : public IEventReceiver
 
56
{
 
57
public:
 
58
 
 
59
        CScreenShotFactory( IrrlichtDevice *device, const c8 * templateName, ISceneNode* node )
 
60
                : Device(device), Number(0), FilenameTemplate(templateName), Node(node)
 
61
        {
 
62
                FilenameTemplate.replace ( '/', '_' );
 
63
                FilenameTemplate.replace ( '\\', '_' );
 
64
        }
 
65
 
 
66
        bool OnEvent(const SEvent& event)
 
67
        {
 
68
                // check if user presses the key F9
 
69
                if ((event.EventType == EET_KEY_INPUT_EVENT) &&
 
70
                                event.KeyInput.PressedDown)
 
71
                {
 
72
                        if (event.KeyInput.Key == KEY_F9)
 
73
                        {
 
74
                                video::IImage* image = Device->getVideoDriver()->createScreenShot();
 
75
                                if (image)
 
76
                                {
 
77
                                        c8 buf[256];
 
78
                                        snprintf(buf, 256, "%s_shot%04d.jpg",
 
79
                                                        FilenameTemplate.c_str(),
 
80
                                                        ++Number);
 
81
                                        Device->getVideoDriver()->writeImageToFile(image, buf, 85 );
 
82
                                        image->drop();
 
83
                                }
 
84
                        }
 
85
                        else
 
86
                        if (event.KeyInput.Key == KEY_F8)
 
87
                        {
 
88
                                if (Node->isDebugDataVisible())
 
89
                                        Node->setDebugDataVisible(scene::EDS_OFF);
 
90
                                else
 
91
                                        Node->setDebugDataVisible(scene::EDS_BBOX_ALL);
 
92
                        }
 
93
                }
 
94
                return false;
 
95
        }
 
96
 
 
97
private:
 
98
        IrrlichtDevice *Device;
 
99
        u32 Number;
 
100
        core::stringc FilenameTemplate;
 
101
        ISceneNode* Node;
 
102
};
 
103
 
 
104
 
 
105
/*
 
106
Ok, lets start.
 
107
*/
 
108
 
 
109
int IRRCALLCONV main(int argc, char* argv[])
 
110
{
 
111
        /*
 
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.
 
117
        */
 
118
 
 
119
        // ask user for driver
 
120
        video::E_DRIVER_TYPE driverType=driverChoiceConsole();
 
121
        if (driverType==video::EDT_COUNT)
 
122
                return 1;
 
123
 
 
124
        // create device and exit if creation failed
 
125
        const core::dimension2du videoDim(800,600);
 
126
 
 
127
        IrrlichtDevice *device = createDevice(driverType, videoDim, 32, false );
 
128
 
 
129
        if (device == 0)
 
130
                return 1; // could not create selected driver.
 
131
 
 
132
        const char* mapname=0;
 
133
        if (argc>2)
 
134
                mapname = argv[2];
 
135
        else
 
136
                mapname = QUAKE3_MAP_NAME;
 
137
 
 
138
        /*
 
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().
 
142
        */
 
143
        video::IVideoDriver* driver = device->getVideoDriver();
 
144
        scene::ISceneManager* smgr = device->getSceneManager();
 
145
        gui::IGUIEnvironment* gui = device->getGUIEnvironment();
 
146
 
 
147
        //! add our private media directory to the file system
 
148
        device->getFileSystem()->addFileArchive("../../media/");
 
149
 
 
150
        /*
 
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.
 
156
        */
 
157
        if (argc>2)
 
158
                device->getFileSystem()->QUAKE3_STORAGE_FORMAT(argv[1]);
 
159
        else
 
160
                device->getFileSystem()->QUAKE3_STORAGE_FORMAT(QUAKE3_STORAGE_1);
 
161
#ifdef QUAKE3_STORAGE_2
 
162
        device->getFileSystem()->QUAKE3_STORAGE_FORMAT(QUAKE3_STORAGE_2);
 
163
#endif
 
164
 
 
165
        // Quake3 Shader controls Z-Writing
 
166
        smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);
 
167
 
 
168
        /*
 
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.
 
183
        */
 
184
        scene::IQ3LevelMesh* const mesh =
 
185
                (scene::IQ3LevelMesh*) smgr->getMesh(mapname);
 
186
 
 
187
        /*
 
188
                add the geometry mesh to the Scene ( polygon & patches )
 
189
                The Geometry mesh is optimised for faster drawing
 
190
        */
 
191
        scene::ISceneNode* node = 0;
 
192
        if (mesh)
 
193
        {
 
194
                scene::IMesh * const geometry = mesh->getMesh(quake3::E_Q3_MESH_GEOMETRY);
 
195
                node = smgr->addOctreeSceneNode(geometry, 0, -1, 4096);
 
196
        }
 
197
 
 
198
        // create an event receiver for making screenshots
 
199
        CScreenShotFactory screenshotFactory(device, mapname, node);
 
200
        device->setEventReceiver(&screenshotFactory);
 
201
 
 
202
        /*
 
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?
 
207
        */
 
208
        if ( mesh )
 
209
        {
 
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);
 
212
 
 
213
#ifdef SHOW_SHADER_NAME
 
214
                gui::IGUIFont *font = device->getGUIEnvironment()->getFont("../../media/fontlucida.png");
 
215
                u32 count = 0;
 
216
#endif
 
217
 
 
218
                for ( u32 i = 0; i!= additional_mesh->getMeshBufferCount(); ++i )
 
219
                {
 
220
                        const IMeshBuffer* meshBuffer = additional_mesh->getMeshBuffer(i);
 
221
                        const video::SMaterial& material = meshBuffer->getMaterial();
 
222
 
 
223
                        // The ShaderIndex is stored in the material parameter
 
224
                        const s32 shaderIndex = (s32) material.MaterialTypeParam2;
 
225
 
 
226
                        // the meshbuffer can be rendered without additional support, or it has no shader
 
227
                        const quake3::IShader *shader = mesh->getShader(shaderIndex);
 
228
                        if (0 == shader)
 
229
                        {
 
230
                                continue;
 
231
                        }
 
232
 
 
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
 
236
                        // would be full...
 
237
                        // quake3::dumpShader ( Shader );
 
238
 
 
239
                        node = smgr->addQuake3SceneNode(meshBuffer, shader);
 
240
 
 
241
#ifdef SHOW_SHADER_NAME
 
242
                        count += 1;
 
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));
 
248
#endif
 
249
                }
 
250
        }
 
251
 
 
252
        /*
 
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).
 
261
        */
 
262
 
 
263
        scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
 
264
 
 
265
        /*
 
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
 
270
        */
 
271
        if ( mesh )
 
272
        {
 
273
                quake3::tQ3EntityList &entityList = mesh->getEntityList();
 
274
 
 
275
                quake3::IEntity search;
 
276
                search.name = "info_player_deathmatch";
 
277
 
 
278
                s32 index = entityList.binary_search(search);
 
279
                if (index >= 0)
 
280
                {
 
281
                        s32 notEndList;
 
282
                        do
 
283
                        {
 
284
                                const quake3::SVarGroup *group = entityList[index].getGroup(1);
 
285
 
 
286
                                u32 parsepos = 0;
 
287
                                const core::vector3df pos =
 
288
                                        quake3::getAsVector3df(group->get("origin"), parsepos);
 
289
 
 
290
                                parsepos = 0;
 
291
                                const f32 angle = quake3::getAsFloat(group->get("angle"), parsepos);
 
292
 
 
293
                                core::vector3df target(0.f, 0.f, 1.f);
 
294
                                target.rotateXZBy(angle);
 
295
 
 
296
                                camera->setPosition(pos);
 
297
                                camera->setTarget(pos + target);
 
298
 
 
299
                                ++index;
 
300
/*
 
301
                                notEndList = (  index < (s32) entityList.size () &&
 
302
                                                                entityList[index].name == search.name &&
 
303
                                                                (device->getTimer()->getRealTime() >> 3 ) & 1
 
304
                                                        );
 
305
*/
 
306
                                notEndList = index == 2;
 
307
                        } while ( notEndList );
 
308
                }
 
309
        }
 
310
 
 
311
        /*
 
312
        The mouse cursor needs not to be visible, so we make it invisible.
 
313
        */
 
314
 
 
315
        device->getCursorControl()->setVisible(false);
 
316
 
 
317
        // load the engine logo
 
318
        gui->addImage(driver->getTexture("irrlichtlogo2.png"),
 
319
                        core::position2d<s32>(10, 10));
 
320
 
 
321
        // show the driver logo
 
322
        const core::position2di pos(videoDim.Width - 128, videoDim.Height - 64);
 
323
 
 
324
        switch ( driverType )
 
325
        {
 
326
                case video::EDT_BURNINGSVIDEO:
 
327
                        gui->addImage(driver->getTexture("burninglogo.png"), pos);
 
328
                        break;
 
329
                case video::EDT_OPENGL:
 
330
                        gui->addImage(driver->getTexture("opengllogo.png"), pos);
 
331
                        break;
 
332
                case video::EDT_DIRECT3D8:
 
333
                case video::EDT_DIRECT3D9:
 
334
                        gui->addImage(driver->getTexture("directxlogo.png"), pos);
 
335
                        break;
 
336
        }
 
337
 
 
338
        /*
 
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.
 
344
        */
 
345
        int lastFPS = -1;
 
346
 
 
347
        while(device->run())
 
348
        if (device->isWindowActive())
 
349
        {
 
350
                driver->beginScene(true, true, video::SColor(255,20,20,40));
 
351
                smgr->drawAll();
 
352
                gui->drawAll();
 
353
                driver->endScene();
 
354
 
 
355
                int fps = driver->getFPS();
 
356
                //if (lastFPS != fps)
 
357
                {
 
358
                        io::IAttributes * const attr = smgr->getParameters();
 
359
                        core::stringw str = L"Q3 [";
 
360
                        str += driver->getName();
 
361
                        str += "] FPS:";
 
362
                        str += fps;
 
363
                        str += " Cull:";
 
364
                        str += attr->getAttributeAsInt("calls");
 
365
                        str += "/";
 
366
                        str += attr->getAttributeAsInt("culled");
 
367
                        str += " Draw: ";
 
368
                        str += attr->getAttributeAsInt("drawn_solid");
 
369
                        str += "/";
 
370
                        str += attr->getAttributeAsInt("drawn_transparent");
 
371
                        str += "/";
 
372
                        str += attr->getAttributeAsInt("drawn_transparent_effect");
 
373
 
 
374
                        device->setWindowCaption(str.c_str());
 
375
                        lastFPS = fps;
 
376
                }
 
377
        }
 
378
 
 
379
        /*
 
380
        In the end, delete the Irrlicht device.
 
381
        */
 
382
        device->drop();
 
383
 
 
384
        return 0;
 
385
}
 
386
 
 
387
/*
 
388
**/