~baltix/+junk/irrlicht-test

« back to all changes in this revision

Viewing changes to examples/11.PerPixelLighting/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 011 Per-Pixel Lighting
 
2
 
 
3
This tutorial shows how to use one of the built in more complex materials in
 
4
irrlicht: Per pixel lighted surfaces using normal maps and parallax mapping. It
 
5
will also show how to use fog and moving particle systems. And don't panic: You
 
6
dont need any experience with shaders to use these materials in Irrlicht.
 
7
 
 
8
At first, we need to include all headers and do the stuff we always do, like in
 
9
nearly all other tutorials.
 
10
*/
 
11
#include <irrlicht.h>
 
12
#include "driverChoice.h"
 
13
 
 
14
using namespace irr;
 
15
 
 
16
#ifdef _MSC_VER
 
17
#pragma comment(lib, "Irrlicht.lib")
 
18
#endif
 
19
 
 
20
/*
 
21
For this example, we need an event receiver, to make it possible for the user
 
22
to switch between the three available material types. In addition, the event
 
23
receiver will create some small GUI window which displays what material is
 
24
currently being used. There is nothing special done in this class, so maybe you
 
25
want to skip reading it.
 
26
*/
 
27
class MyEventReceiver : public IEventReceiver
 
28
{
 
29
public:
 
30
 
 
31
        MyEventReceiver(scene::ISceneNode* room,scene::ISceneNode* earth,
 
32
                gui::IGUIEnvironment* env, video::IVideoDriver* driver)
 
33
        {
 
34
                // store pointer to room so we can change its drawing mode
 
35
                Room = room;
 
36
                Earth = earth;
 
37
                Driver = driver;
 
38
 
 
39
                // set a nicer font
 
40
                gui::IGUISkin* skin = env->getSkin();
 
41
                gui::IGUIFont* font = env->getFont("../../media/fonthaettenschweiler.bmp");
 
42
                if (font)
 
43
                        skin->setFont(font);
 
44
 
 
45
                // add window and listbox
 
46
                gui::IGUIWindow* window = env->addWindow(
 
47
                        core::rect<s32>(460,375,630,470), false, L"Use 'E' + 'R' to change");
 
48
 
 
49
                ListBox = env->addListBox(
 
50
                        core::rect<s32>(2,22,165,88), window);
 
51
 
 
52
                ListBox->addItem(L"Diffuse");
 
53
                ListBox->addItem(L"Bump mapping");
 
54
                ListBox->addItem(L"Parallax mapping");
 
55
                ListBox->setSelected(1);
 
56
 
 
57
                // create problem text
 
58
                ProblemText = env->addStaticText(
 
59
                        L"Your hardware or this renderer is not able to use the "\
 
60
                        L"needed shaders for this material. Using fall back materials.",
 
61
                        core::rect<s32>(150,20,470,80));
 
62
 
 
63
                ProblemText->setOverrideColor(video::SColor(100,255,255,255));
 
64
 
 
65
                // set start material (prefer parallax mapping if available)
 
66
                video::IMaterialRenderer* renderer =
 
67
                        Driver->getMaterialRenderer(video::EMT_PARALLAX_MAP_SOLID);
 
68
                if (renderer && renderer->getRenderCapability() == 0)
 
69
                        ListBox->setSelected(2);
 
70
 
 
71
                // set the material which is selected in the listbox
 
72
                setMaterial();
 
73
        }
 
74
 
 
75
        bool OnEvent(const SEvent& event)
 
76
        {
 
77
                // check if user presses the key 'E' or 'R'
 
78
                if (event.EventType == irr::EET_KEY_INPUT_EVENT &&
 
79
                        !event.KeyInput.PressedDown && Room && ListBox)
 
80
                {
 
81
                        // change selected item in listbox
 
82
 
 
83
                        int sel = ListBox->getSelected();
 
84
                        if (event.KeyInput.Key == irr::KEY_KEY_R)
 
85
                                ++sel;
 
86
                        else
 
87
                        if (event.KeyInput.Key == irr::KEY_KEY_E)
 
88
                                --sel;
 
89
                        else
 
90
                                return false;
 
91
 
 
92
                        if (sel > 2) sel = 0;
 
93
                        if (sel < 0) sel = 2;
 
94
                        ListBox->setSelected(sel);
 
95
 
 
96
                        // set the material which is selected in the listbox
 
97
                        setMaterial();
 
98
                }
 
99
 
 
100
                return false;
 
101
        }
 
102
 
 
103
private:
 
104
 
 
105
        // sets the material of the room mesh the the one set in the
 
106
        // list box.
 
107
        void setMaterial()
 
108
        {
 
109
                video::E_MATERIAL_TYPE type = video::EMT_SOLID;
 
110
 
 
111
                // change material setting
 
112
                switch(ListBox->getSelected())
 
113
                {
 
114
                case 0: type = video::EMT_SOLID;
 
115
                        break;
 
116
                case 1: type = video::EMT_NORMAL_MAP_SOLID;
 
117
                        break;
 
118
                case 2: type = video::EMT_PARALLAX_MAP_SOLID;
 
119
                        break;
 
120
                }
 
121
 
 
122
                Room->setMaterialType(type);
 
123
 
 
124
                // change material setting
 
125
                switch(ListBox->getSelected())
 
126
                {
 
127
                case 0: type = video::EMT_TRANSPARENT_VERTEX_ALPHA;
 
128
                        break;
 
129
                case 1: type = video::EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA;
 
130
                        break;
 
131
                case 2: type = video::EMT_PARALLAX_MAP_TRANSPARENT_VERTEX_ALPHA;
 
132
                        break;
 
133
                }
 
134
 
 
135
                Earth->setMaterialType(type);
 
136
 
 
137
                /*
 
138
                We need to add a warning if the materials will not be able to
 
139
                be displayed 100% correctly. This is no problem, they will be
 
140
                renderered using fall back materials, but at least the user
 
141
                should know that it would look better on better hardware. We
 
142
                simply check if the material renderer is able to draw at full
 
143
                quality on the current hardware. The
 
144
                IMaterialRenderer::getRenderCapability() returns 0 if this is
 
145
                the case.
 
146
                */
 
147
                video::IMaterialRenderer* renderer = Driver->getMaterialRenderer(type);
 
148
 
 
149
                // display some problem text when problem
 
150
                if (!renderer || renderer->getRenderCapability() != 0)
 
151
                        ProblemText->setVisible(true);
 
152
                else
 
153
                        ProblemText->setVisible(false);
 
154
        }
 
155
 
 
156
private:
 
157
 
 
158
        gui::IGUIStaticText* ProblemText;
 
159
        gui::IGUIListBox* ListBox;
 
160
 
 
161
        scene::ISceneNode* Room;
 
162
        scene::ISceneNode* Earth;
 
163
        video::IVideoDriver* Driver;
 
164
};
 
165
 
 
166
 
 
167
/*
 
168
Now for the real fun. We create an Irrlicht Device and start to setup the scene.
 
169
*/
 
170
int main()
 
171
{
 
172
        // let user select driver type
 
173
        video::E_DRIVER_TYPE driverType=driverChoiceConsole();
 
174
        if (driverType==video::EDT_COUNT)
 
175
                return 1;
 
176
 
 
177
        // create device
 
178
 
 
179
        IrrlichtDevice* device = createDevice(driverType,
 
180
                        core::dimension2d<u32>(640, 480));
 
181
 
 
182
        if (device == 0)
 
183
                return 1; // could not create selected driver.
 
184
 
 
185
 
 
186
        /*
 
187
        Before we start with the interesting stuff, we do some simple things:
 
188
        Store pointers to the most important parts of the engine (video driver,
 
189
        scene manager, gui environment) to safe us from typing too much, add an
 
190
        irrlicht engine logo to the window and a user controlled first person
 
191
        shooter style camera. Also, we let the engine know that it should store
 
192
        all textures in 32 bit. This necessary because for parallax mapping, we
 
193
        need 32 bit textures.
 
194
        */
 
195
 
 
196
        video::IVideoDriver* driver = device->getVideoDriver();
 
197
        scene::ISceneManager* smgr = device->getSceneManager();
 
198
        gui::IGUIEnvironment* env = device->getGUIEnvironment();
 
199
 
 
200
        driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
 
201
 
 
202
        // add irrlicht logo
 
203
        env->addImage(driver->getTexture("../../media/irrlichtlogo3.png"),
 
204
                core::position2d<s32>(10,10));
 
205
 
 
206
        // add camera
 
207
        scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
 
208
        camera->setPosition(core::vector3df(-200,200,-200));
 
209
 
 
210
        // disable mouse cursor
 
211
        device->getCursorControl()->setVisible(false);
 
212
 
 
213
        /*
 
214
        Because we want the whole scene to look a little bit scarier, we add
 
215
        some fog to it. This is done by a call to IVideoDriver::setFog(). There
 
216
        you can set various fog settings. In this example, we use pixel fog,
 
217
        because it will work well with the materials we'll use in this example.
 
218
        Please note that you will have to set the material flag EMF_FOG_ENABLE
 
219
        to 'true' in every scene node which should be affected by this fog.
 
220
        */
 
221
        driver->setFog(video::SColor(0,138,125,81), video::EFT_FOG_LINEAR, 250, 1000, .003f, true, false);
 
222
 
 
223
        /*
 
224
        To be able to display something interesting, we load a mesh from a .3ds
 
225
        file which is a room I modeled with anim8or. It is the same room as
 
226
        from the specialFX example. Maybe you remember from that tutorial, I am
 
227
        no good modeler at all and so I totally messed up the texture mapping
 
228
        in this model, but we can simply repair it with the
 
229
        IMeshManipulator::makePlanarTextureMapping() method.
 
230
        */
 
231
 
 
232
        scene::IAnimatedMesh* roomMesh = smgr->getMesh("../../media/room.3ds");
 
233
        scene::ISceneNode* room = 0;
 
234
        scene::ISceneNode* earth = 0;
 
235
 
 
236
        if (roomMesh)
 
237
        {
 
238
                // The Room mesh doesn't have proper Texture Mapping on the
 
239
                // floor, so we can recreate them on runtime
 
240
                smgr->getMeshManipulator()->makePlanarTextureMapping(
 
241
                                roomMesh->getMesh(0), 0.003f);
 
242
 
 
243
                /*
 
244
                Now for the first exciting thing: If we successfully loaded the
 
245
                mesh we need to apply textures to it. Because we want this room
 
246
                to be displayed with a very cool material, we have to do a
 
247
                little bit more than just set the textures. Instead of only
 
248
                loading a color map as usual, we also load a height map which
 
249
                is simply a grayscale texture. From this height map, we create
 
250
                a normal map which we will set as second texture of the room.
 
251
                If you already have a normal map, you could directly set it,
 
252
                but I simply didn't find a nice normal map for this texture.
 
253
                The normal map texture is being generated by the
 
254
                makeNormalMapTexture method of the VideoDriver. The second
 
255
                parameter specifies the height of the heightmap. If you set it
 
256
                to a bigger value, the map will look more rocky.
 
257
                */
 
258
 
 
259
                video::ITexture* normalMap =
 
260
                        driver->getTexture("../../media/rockwall_height.bmp");
 
261
 
 
262
                if (normalMap)
 
263
                        driver->makeNormalMapTexture(normalMap, 9.0f);
 
264
/*
 
265
                // The Normal Map and the displacement map/height map in the alpha channel
 
266
                video::ITexture* normalMap = 
 
267
                        driver->getTexture("../../media/rockwall_NRM.tga");
 
268
*/
 
269
                /*
 
270
                But just setting color and normal map is not everything. The
 
271
                material we want to use needs some additional informations per
 
272
                vertex like tangents and binormals. Because we are too lazy to
 
273
                calculate that information now, we let Irrlicht do this for us.
 
274
                That's why we call IMeshManipulator::createMeshWithTangents().
 
275
                It creates a mesh copy with tangents and binormals from another
 
276
                mesh. After we've done that, we simply create a standard
 
277
                mesh scene node with this mesh copy, set color and normal map
 
278
                and adjust some other material settings. Note that we set
 
279
                EMF_FOG_ENABLE to true to enable fog in the room.
 
280
                */
 
281
 
 
282
                scene::IMesh* tangentMesh = smgr->getMeshManipulator()->
 
283
                                createMeshWithTangents(roomMesh->getMesh(0));
 
284
 
 
285
                room = smgr->addMeshSceneNode(tangentMesh);
 
286
                room->setMaterialTexture(0,
 
287
                                driver->getTexture("../../media/rockwall.jpg"));
 
288
                room->setMaterialTexture(1, normalMap);
 
289
 
 
290
                // Stones don't glitter..
 
291
                room->getMaterial(0).SpecularColor.set(0,0,0,0);
 
292
                room->getMaterial(0).Shininess = 0.f;
 
293
 
 
294
                room->setMaterialFlag(video::EMF_FOG_ENABLE, true);
 
295
                room->setMaterialType(video::EMT_PARALLAX_MAP_SOLID);
 
296
                // adjust height for parallax effect
 
297
                room->getMaterial(0).MaterialTypeParam = 1.f / 64.f;
 
298
 
 
299
                // drop mesh because we created it with a create.. call.
 
300
                tangentMesh->drop();
 
301
        }
 
302
 
 
303
        /*
 
304
        After we've created a room shaded by per pixel lighting, we add a
 
305
        sphere into it with the same material, but we'll make it transparent.
 
306
        In addition, because the sphere looks somehow like a familiar planet,
 
307
        we make it rotate. The procedure is similar as before. The difference
 
308
        is that we are loading the mesh from an .x file which already contains
 
309
        a color map so we do not need to load it manually. But the sphere is a
 
310
        little bit too small for our needs, so we scale it by the factor 50.
 
311
        */
 
312
 
 
313
        // add earth sphere
 
314
 
 
315
        scene::IAnimatedMesh* earthMesh = smgr->getMesh("../../media/earth.x");
 
316
        if (earthMesh)
 
317
        {
 
318
                //perform various task with the mesh manipulator
 
319
                scene::IMeshManipulator *manipulator = smgr->getMeshManipulator();
 
320
 
 
321
                // create mesh copy with tangent informations from original earth.x mesh
 
322
                scene::IMesh* tangentSphereMesh =
 
323
                        manipulator->createMeshWithTangents(earthMesh->getMesh(0));
 
324
 
 
325
                // set the alpha value of all vertices to 200
 
326
                manipulator->setVertexColorAlpha(tangentSphereMesh, 200);
 
327
 
 
328
                // scale the mesh by factor 50
 
329
                core::matrix4 m;
 
330
                m.setScale ( core::vector3df(50,50,50) );
 
331
                manipulator->transform( tangentSphereMesh, m );
 
332
 
 
333
                earth = smgr->addMeshSceneNode(tangentSphereMesh);
 
334
 
 
335
                earth->setPosition(core::vector3df(-70,130,45));
 
336
 
 
337
                // load heightmap, create normal map from it and set it
 
338
                video::ITexture* earthNormalMap = driver->getTexture("../../media/earthbump.jpg");
 
339
                if (earthNormalMap)
 
340
                {
 
341
                        driver->makeNormalMapTexture(earthNormalMap, 20.0f);
 
342
                        earth->setMaterialTexture(1, earthNormalMap);
 
343
                        earth->setMaterialType(video::EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA);
 
344
                }
 
345
 
 
346
                // adjust material settings
 
347
                earth->setMaterialFlag(video::EMF_FOG_ENABLE, true);
 
348
 
 
349
                // add rotation animator
 
350
                scene::ISceneNodeAnimator* anim =
 
351
                        smgr->createRotationAnimator(core::vector3df(0,0.1f,0));
 
352
                earth->addAnimator(anim);
 
353
                anim->drop();
 
354
 
 
355
                // drop mesh because we created it with a create.. call.
 
356
                tangentSphereMesh->drop();
 
357
        }
 
358
 
 
359
        /*
 
360
        Per pixel lighted materials only look cool when there are moving
 
361
        lights. So we add some. And because moving lights alone are so boring,
 
362
        we add billboards to them, and a whole particle system to one of them.
 
363
        We start with the first light which is red and has only the billboard
 
364
        attached.
 
365
        */
 
366
 
 
367
        // add light 1 (more green)
 
368
        scene::ILightSceneNode* light1 =
 
369
                smgr->addLightSceneNode(0, core::vector3df(0,0,0),
 
370
                video::SColorf(0.5f, 1.0f, 0.5f, 0.0f), 800.0f);
 
371
 
 
372
        light1->setDebugDataVisible ( scene::EDS_BBOX );
 
373
 
 
374
 
 
375
        // add fly circle animator to light 1
 
376
        scene::ISceneNodeAnimator* anim =
 
377
                smgr->createFlyCircleAnimator (core::vector3df(50,300,0),190.0f, -0.003f);
 
378
        light1->addAnimator(anim);
 
379
        anim->drop();
 
380
 
 
381
        // attach billboard to the light
 
382
        scene::ISceneNode* bill =
 
383
                smgr->addBillboardSceneNode(light1, core::dimension2d<f32>(60, 60));
 
384
 
 
385
        bill->setMaterialFlag(video::EMF_LIGHTING, false);
 
386
        bill->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
 
387
        bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
 
388
        bill->setMaterialTexture(0, driver->getTexture("../../media/particlegreen.jpg"));
 
389
 
 
390
        /*
 
391
        Now the same again, with the second light. The difference is that we
 
392
        add a particle system to it too. And because the light moves, the
 
393
        particles of the particlesystem will follow. If you want to know more
 
394
        about how particle systems are created in Irrlicht, take a look at the
 
395
        specialFx example. Maybe you will have noticed that we only add 2
 
396
        lights, this has a simple reason: The low end version of this material
 
397
        was written in ps1.1 and vs1.1, which doesn't allow more lights. You
 
398
        could add a third light to the scene, but it won't be used to shade the
 
399
        walls. But of course, this will change in future versions of Irrlicht
 
400
        where higher versions of pixel/vertex shaders will be implemented too.
 
401
        */
 
402
 
 
403
        // add light 2 (red)
 
404
        scene::ISceneNode* light2 =
 
405
                smgr->addLightSceneNode(0, core::vector3df(0,0,0),
 
406
                video::SColorf(1.0f, 0.2f, 0.2f, 0.0f), 800.0f);
 
407
 
 
408
        // add fly circle animator to light 2
 
409
        anim = smgr->createFlyCircleAnimator(core::vector3df(0,150,0), 200.0f,
 
410
                        0.001f, core::vector3df(0.2f, 0.9f, 0.f));
 
411
        light2->addAnimator(anim);
 
412
        anim->drop();
 
413
 
 
414
        // attach billboard to light
 
415
        bill = smgr->addBillboardSceneNode(light2, core::dimension2d<f32>(120, 120));
 
416
        bill->setMaterialFlag(video::EMF_LIGHTING, false);
 
417
        bill->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
 
418
        bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
 
419
        bill->setMaterialTexture(0, driver->getTexture("../../media/particlered.bmp"));
 
420
 
 
421
        // add particle system
 
422
        scene::IParticleSystemSceneNode* ps =
 
423
                smgr->addParticleSystemSceneNode(false, light2);
 
424
 
 
425
        // create and set emitter
 
426
        scene::IParticleEmitter* em = ps->createBoxEmitter(
 
427
                core::aabbox3d<f32>(-3,0,-3,3,1,3),
 
428
                core::vector3df(0.0f,0.03f,0.0f),
 
429
                80,100,
 
430
                video::SColor(10,255,255,255), video::SColor(10,255,255,255),
 
431
                400,1100);
 
432
        em->setMinStartSize(core::dimension2d<f32>(30.0f, 40.0f));
 
433
        em->setMaxStartSize(core::dimension2d<f32>(30.0f, 40.0f));
 
434
 
 
435
        ps->setEmitter(em);
 
436
        em->drop();
 
437
 
 
438
        // create and set affector
 
439
        scene::IParticleAffector* paf = ps->createFadeOutParticleAffector();
 
440
        ps->addAffector(paf);
 
441
        paf->drop();
 
442
 
 
443
        // adjust some material settings
 
444
        ps->setMaterialFlag(video::EMF_LIGHTING, false);
 
445
        ps->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
 
446
        ps->setMaterialTexture(0, driver->getTexture("../../media/fireball.bmp"));
 
447
        ps->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
 
448
 
 
449
        MyEventReceiver receiver(room, earth, env, driver);
 
450
        device->setEventReceiver(&receiver);
 
451
 
 
452
        /*
 
453
        Finally, draw everything. That's it.
 
454
        */
 
455
 
 
456
        int lastFPS = -1;
 
457
 
 
458
        while(device->run())
 
459
        if (device->isWindowActive())
 
460
        {
 
461
                driver->beginScene(true, true, 0);
 
462
 
 
463
                smgr->drawAll();
 
464
                env->drawAll();
 
465
 
 
466
                driver->endScene();
 
467
 
 
468
                int fps = driver->getFPS();
 
469
 
 
470
                if (lastFPS != fps)
 
471
                {
 
472
                        core::stringw str = L"Per pixel lighting example - Irrlicht Engine [";
 
473
                        str += driver->getName();
 
474
                        str += "] FPS:";
 
475
                        str += fps;
 
476
 
 
477
                        device->setWindowCaption(str.c_str());
 
478
                        lastFPS = fps;
 
479
                }
 
480
        }
 
481
 
 
482
        device->drop();
 
483
 
 
484
        return 0;
 
485
}
 
486
 
 
487
/*
 
488
**/