~baltix/+junk/irrlicht-test

« back to all changes in this revision

Viewing changes to examples/20.ManagedLights/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 020 Managed Lights
 
2
 
 
3
Written by Colin MacDonald. This tutorial explains the use of the Light Manager
 
4
of Irrlicht. It enables the use of more dynamic light sources than the actual
 
5
hardware supports. Further applications of the Light Manager, such as per scene
 
6
node callbacks, are left out for simplicity of the example.
 
7
*/
 
8
 
 
9
#include <irrlicht.h>
 
10
#include "driverChoice.h"
 
11
 
 
12
using namespace irr;
 
13
using namespace core;
 
14
 
 
15
#if defined(_MSC_VER)
 
16
#pragma comment(lib, "Irrlicht.lib")
 
17
#endif // MSC_VER
 
18
 
 
19
/*
 
20
    Normally, you are limited to 8 dynamic lights per scene: this is a hardware limit.  If you
 
21
    want to use more dynamic lights in your scene, then you can register an optional light
 
22
    manager that allows you to to turn lights on and off at specific point during rendering.
 
23
    You are still limited to 8 lights, but the limit is per scene node.
 
24
 
 
25
    This is completely optional: if you do not register a light manager, then a default
 
26
    distance-based scheme will be used to prioritise hardware lights based on their distance
 
27
    from the active camera.
 
28
 
 
29
        NO_MANAGEMENT disables the light manager and shows Irrlicht's default light behaviour.
 
30
    The 8 lights nearest to the camera will be turned on, and other lights will be turned off.
 
31
    In this example, this produces a funky looking but incoherent light display.
 
32
 
 
33
        LIGHTS_NEAREST_NODE shows an implementation that turns on a limited number of lights
 
34
    per mesh scene node.  If finds the 3 lights that are nearest to the node being rendered,
 
35
    and turns them on, turning all other lights off.  This works, but as it operates on every
 
36
    light for every node, it does not scale well with many lights.  The flickering you can see
 
37
    in this demo is due to the lights swapping their relative positions from the cubes
 
38
    (a deliberate demonstration of the limitations of this technique).
 
39
 
 
40
        LIGHTS_IN_ZONE shows a technique for turning on lights based on a 'zone'. Each empty scene
 
41
    node is considered to be the parent of a zone.  When nodes are rendered, they turn off all
 
42
    lights, then find their parent 'zone' and turn on all lights that are inside that zone, i.e.
 
43
        are  descendents of it in the scene graph.  This produces true 'local' lighting for each cube
 
44
    in this example.  You could use a similar technique to locally light all meshes in (e.g.)
 
45
    a room, without the lights spilling out to other rooms.
 
46
 
 
47
        This light manager is also an event receiver; this is purely for simplicity in this example,
 
48
    it's neither necessary nor recommended for a real application.
 
49
*/
 
50
class CMyLightManager : public scene::ILightManager, public IEventReceiver
 
51
{
 
52
        typedef enum
 
53
        {
 
54
                NO_MANAGEMENT,
 
55
                LIGHTS_NEAREST_NODE,
 
56
                LIGHTS_IN_ZONE
 
57
        }
 
58
        LightManagementMode;
 
59
 
 
60
        LightManagementMode Mode;
 
61
        LightManagementMode RequestedMode;
 
62
 
 
63
        // These data represent the state information that this light manager
 
64
        // is interested in.
 
65
        scene::ISceneManager * SceneManager;
 
66
        core::array<scene::ISceneNode*> * SceneLightList;
 
67
        scene::E_SCENE_NODE_RENDER_PASS CurrentRenderPass;
 
68
        scene::ISceneNode * CurrentSceneNode;
 
69
 
 
70
public:
 
71
        CMyLightManager(scene::ISceneManager* sceneManager)
 
72
                : Mode(NO_MANAGEMENT), RequestedMode(NO_MANAGEMENT),
 
73
                SceneManager(sceneManager), SceneLightList(0),
 
74
                CurrentRenderPass(scene::ESNRP_NONE), CurrentSceneNode(0)
 
75
        { }
 
76
 
 
77
        // The input receiver interface, which just switches light management strategy
 
78
        bool OnEvent(const SEvent & event)
 
79
        {
 
80
                bool handled = false;
 
81
 
 
82
                if (event.EventType == irr::EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
 
83
                {
 
84
                        handled = true;
 
85
                        switch(event.KeyInput.Key)
 
86
                        {
 
87
                        case irr::KEY_KEY_1:
 
88
                                RequestedMode = NO_MANAGEMENT;
 
89
                                break;
 
90
                        case irr::KEY_KEY_2:
 
91
                                RequestedMode = LIGHTS_NEAREST_NODE;
 
92
                                break;
 
93
                        case irr::KEY_KEY_3:
 
94
                                RequestedMode = LIGHTS_IN_ZONE;
 
95
                                break;
 
96
                        default:
 
97
                                handled = false;
 
98
                                break;
 
99
                        }
 
100
 
 
101
                        if(NO_MANAGEMENT == RequestedMode)
 
102
                                SceneManager->setLightManager(0); // Show that it's safe to register the light manager
 
103
                        else
 
104
                                SceneManager->setLightManager(this);
 
105
                }
 
106
 
 
107
                return handled;
 
108
        }
 
109
 
 
110
 
 
111
        // This is called before the first scene node is rendered.
 
112
        virtual void OnPreRender(core::array<scene::ISceneNode*> & lightList)
 
113
        {
 
114
                // Update the mode; changing it here ensures that it's consistent throughout a render
 
115
                Mode = RequestedMode;
 
116
 
 
117
                // Store the light list. I am free to alter this list until the end of OnPostRender().
 
118
                SceneLightList = &lightList;
 
119
        }
 
120
 
 
121
        // Called after the last scene node is rendered.
 
122
        virtual void OnPostRender()
 
123
        {
 
124
                // Since light management might be switched off in the event handler, we'll turn all
 
125
                // lights on to ensure that they are in a consistent state. You wouldn't normally have
 
126
                // to do this when using a light manager, since you'd continue to do light management
 
127
                // yourself.
 
128
                for (u32 i = 0; i < SceneLightList->size(); i++)
 
129
                        (*SceneLightList)[i]->setVisible(true);
 
130
        }
 
131
 
 
132
        virtual void OnRenderPassPreRender(scene::E_SCENE_NODE_RENDER_PASS renderPass)
 
133
        {
 
134
                // I don't have to do anything here except remember which render pass I am in.
 
135
                CurrentRenderPass = renderPass;
 
136
        }
 
137
 
 
138
        virtual void OnRenderPassPostRender(scene::E_SCENE_NODE_RENDER_PASS renderPass)
 
139
        {
 
140
                // I only want solid nodes to be lit, so after the solid pass, turn all lights off.
 
141
                if (scene::ESNRP_SOLID == renderPass)
 
142
                {
 
143
                        for (u32 i = 0; i < SceneLightList->size(); ++i)
 
144
                                (*SceneLightList)[i]->setVisible(false);
 
145
                }
 
146
        }
 
147
 
 
148
        // This is called before the specified scene node is rendered
 
149
        virtual void OnNodePreRender(scene::ISceneNode* node)
 
150
        {
 
151
                CurrentSceneNode = node;
 
152
 
 
153
                // This light manager only considers solid objects, but you are free to manipulate
 
154
                // lights during any phase, depending on your requirements.
 
155
                if (scene::ESNRP_SOLID != CurrentRenderPass)
 
156
                        return;
 
157
 
 
158
                // And in fact for this example, I only want to consider lighting for cube scene
 
159
                // nodes.  You will probably want to deal with lighting for (at least) mesh /
 
160
                // animated mesh scene nodes as well.
 
161
                if (node->getType() != scene::ESNT_CUBE)
 
162
                        return;
 
163
 
 
164
                if (LIGHTS_NEAREST_NODE == Mode)
 
165
                {
 
166
                        // This is a naive implementation that prioritises every light in the scene
 
167
                        // by its proximity to the node being rendered.  This produces some flickering
 
168
                        // when lights orbit closer to a cube than its 'zone' lights.
 
169
                        const vector3df nodePosition = node->getAbsolutePosition();
 
170
 
 
171
                        // Sort the light list by prioritising them based on their distance from the node
 
172
                        // that's about to be rendered.
 
173
                        array<LightDistanceElement> sortingArray;
 
174
                        sortingArray.reallocate(SceneLightList->size());
 
175
 
 
176
                        u32 i;
 
177
                        for(i = 0; i < SceneLightList->size(); ++i)
 
178
                        {
 
179
                                scene::ISceneNode* lightNode = (*SceneLightList)[i];
 
180
                                const f64 distance = lightNode->getAbsolutePosition().getDistanceFromSQ(nodePosition);
 
181
                                sortingArray.push_back(LightDistanceElement(lightNode, distance));
 
182
                        }
 
183
 
 
184
                        sortingArray.sort();
 
185
 
 
186
                        // The list is now sorted by proximity to the node.
 
187
                        // Turn on the three nearest lights, and turn the others off.
 
188
                        for(i = 0; i < sortingArray.size(); ++i)
 
189
                                sortingArray[i].node->setVisible(i < 3);
 
190
                }
 
191
                else if(LIGHTS_IN_ZONE == Mode)
 
192
                {
 
193
                        // Empty scene nodes are used to represent 'zones'.  For each solid mesh that
 
194
                        // is being rendered, turn off all lights, then find its 'zone' parent, and turn
 
195
                        // on all lights that are found under that node in the scene graph.
 
196
                        // This is a general purpose algorithm that doesn't use any special
 
197
                        // knowledge of how this particular scene graph is organised.
 
198
                        for (u32 i = 0; i < SceneLightList->size(); ++i)
 
199
                        {
 
200
                                if ((*SceneLightList)[i]->getType() != scene::ESNT_LIGHT)
 
201
                                        continue;
 
202
                                scene::ILightSceneNode* lightNode = static_cast<scene::ILightSceneNode*>((*SceneLightList)[i]);
 
203
                                video::SLight & lightData = lightNode->getLightData();
 
204
 
 
205
                                if (video::ELT_DIRECTIONAL != lightData.Type)
 
206
                                        lightNode->setVisible(false);
 
207
                        }
 
208
 
 
209
                        scene::ISceneNode * parentZone = findZone(node);
 
210
                        if (parentZone)
 
211
                                turnOnZoneLights(parentZone);
 
212
                }
 
213
        }
 
214
 
 
215
        // Called after the specified scene node is rendered
 
216
        virtual void OnNodePostRender(scene::ISceneNode* node)
 
217
        {
 
218
                // I don't need to do any light management after individual node rendering.
 
219
        }
 
220
 
 
221
private:
 
222
 
 
223
        // Find the empty scene node that is the parent of the specified node
 
224
        scene::ISceneNode * findZone(scene::ISceneNode * node)
 
225
        {
 
226
                if (!node)
 
227
                        return 0;
 
228
 
 
229
                if (node->getType() == scene::ESNT_EMPTY)
 
230
                        return node;
 
231
 
 
232
                return findZone(node->getParent());
 
233
        }
 
234
 
 
235
        // Turn on all lights that are children (directly or indirectly) of the
 
236
        // specified scene node.
 
237
        void turnOnZoneLights(scene::ISceneNode * node)
 
238
        {
 
239
                core::list<scene::ISceneNode*> const & children = node->getChildren();
 
240
                for (core::list<scene::ISceneNode*>::ConstIterator child = children.begin();
 
241
                        child != children.end(); ++child)
 
242
                {
 
243
                        if ((*child)->getType() == scene::ESNT_LIGHT)
 
244
                                (*child)->setVisible(true);
 
245
                        else // Assume that lights don't have any children that are also lights
 
246
                                turnOnZoneLights(*child);
 
247
                }
 
248
        }
 
249
 
 
250
 
 
251
        // A utility class to aid in sorting scene nodes into a distance order
 
252
        class LightDistanceElement
 
253
        {
 
254
        public:
 
255
                LightDistanceElement() {};
 
256
 
 
257
                LightDistanceElement(scene::ISceneNode* n, f64 d)
 
258
                        : node(n), distance(d) { }
 
259
 
 
260
                scene::ISceneNode* node;
 
261
                f64 distance;
 
262
 
 
263
                // Lower distance elements are sorted to the start of the array
 
264
                bool operator < (const LightDistanceElement& other) const
 
265
                {
 
266
                        return (distance < other.distance);
 
267
                }
 
268
        };
 
269
};
 
270
 
 
271
 
 
272
/*
 
273
*/
 
274
int main(int argumentCount, char * argumentValues[])
 
275
{
 
276
        // ask user for driver
 
277
        video::E_DRIVER_TYPE driverType=driverChoiceConsole();
 
278
        if (driverType==video::EDT_COUNT)
 
279
                return 1;
 
280
 
 
281
        IrrlichtDevice *device = createDevice(driverType,
 
282
                        dimension2d<u32>(640, 480), 32);
 
283
 
 
284
        if(!device)
 
285
                return -1;
 
286
 
 
287
        f32 const lightRadius = 60.f; // Enough to reach the far side of each 'zone'
 
288
 
 
289
        video::IVideoDriver* driver = device->getVideoDriver();
 
290
        scene::ISceneManager* smgr = device->getSceneManager();
 
291
        gui::IGUIEnvironment* guienv = device->getGUIEnvironment();
 
292
 
 
293
        gui::IGUISkin* skin = guienv->getSkin();
 
294
        if (skin)
 
295
        {
 
296
                skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255, 255, 255, 255));
 
297
                gui::IGUIFont* font = guienv->getFont("../../media/fontlucida.png");
 
298
                if(font)
 
299
                        skin->setFont(font);
 
300
        }
 
301
 
 
302
        guienv->addStaticText(L"1 - No light management", core::rect<s32>(10,10,200,30));
 
303
        guienv->addStaticText(L"2 - Closest 3 lights", core::rect<s32>(10,30,200,50));
 
304
        guienv->addStaticText(L"3 - Lights in zone", core::rect<s32>(10,50,200,70));
 
305
 
 
306
/*
 
307
Add several "zones".  You could use this technique to light individual rooms, for example.
 
308
*/
 
309
        for(f32 zoneX = -100.f; zoneX <= 100.f; zoneX += 50.f)
 
310
                for(f32 zoneY = -60.f; zoneY <= 60.f; zoneY += 60.f)
 
311
                {
 
312
                        // Start with an empty scene node, which we will use to represent a zone.
 
313
                        scene::ISceneNode * zoneRoot = smgr->addEmptySceneNode();
 
314
                        zoneRoot->setPosition(vector3df(zoneX, zoneY, 0));
 
315
 
 
316
                        // Each zone contains a rotating cube
 
317
                        scene::IMeshSceneNode * node = smgr->addCubeSceneNode(15, zoneRoot);
 
318
                        scene::ISceneNodeAnimator * rotation = smgr->createRotationAnimator(vector3df(0.25f, 0.5f, 0.75f));
 
319
                        node->addAnimator(rotation);
 
320
                        rotation->drop();
 
321
 
 
322
                        // And each cube has three lights attached to it.  The lights are attached to billboards so
 
323
                        // that we can see where they are.  The billboards are attached to the cube, so that the
 
324
                        // lights are indirect descendents of the same empty scene node as the cube.
 
325
                        scene::IBillboardSceneNode * billboard = smgr->addBillboardSceneNode(node);
 
326
                        billboard->setPosition(vector3df(0, -14, 30));
 
327
                        billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
 
328
                        billboard->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
 
329
                        billboard->setMaterialFlag(video::EMF_LIGHTING, false);
 
330
                        scene::ILightSceneNode * light = smgr->addLightSceneNode(billboard, vector3df(0, 0, 0), video::SColorf(1, 0, 0), lightRadius);
 
331
 
 
332
                        billboard = smgr->addBillboardSceneNode(node);
 
333
                        billboard->setPosition(vector3df(-21, -14, -21));
 
334
                        billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
 
335
                        billboard->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
 
336
                        billboard->setMaterialFlag(video::EMF_LIGHTING, false);
 
337
                        light = smgr->addLightSceneNode(billboard, vector3df(0, 0, 0), video::SColorf(0, 1, 0), lightRadius);
 
338
 
 
339
                        billboard = smgr->addBillboardSceneNode(node);
 
340
                        billboard->setPosition(vector3df(21, -14, -21));
 
341
                        billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
 
342
                        billboard->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
 
343
                        billboard->setMaterialFlag(video::EMF_LIGHTING, false);
 
344
                        light = smgr->addLightSceneNode(billboard, vector3df(0, 0, 0), video::SColorf(0, 0, 1), lightRadius);
 
345
 
 
346
                        // Each cube also has a smaller cube rotating around it, to show that the cubes are being
 
347
                        // lit by the lights in their 'zone', not just lights that are their direct children.
 
348
                        node = smgr->addCubeSceneNode(5, node);
 
349
                        node->setPosition(vector3df(0, 21, 0));
 
350
                }
 
351
 
 
352
        smgr->addCameraSceneNode(0, vector3df(0,0,-130), vector3df(0,0,0));
 
353
 
 
354
        CMyLightManager * myLightManager = new CMyLightManager(smgr);
 
355
        smgr->setLightManager(0); // This is the default: we won't do light management until told to do it.
 
356
        device->setEventReceiver(myLightManager);
 
357
 
 
358
        int lastFps = -1;
 
359
 
 
360
        while(device->run())
 
361
        {
 
362
                driver->beginScene(true, true, video::SColor(255,100,101,140));
 
363
                smgr->drawAll();
 
364
                guienv->drawAll();
 
365
                driver->endScene();
 
366
 
 
367
                int fps = driver->getFPS();
 
368
                if(fps != lastFps)
 
369
                {
 
370
                        lastFps = fps;
 
371
                        core::stringw str = L"Managed Lights [";
 
372
                        str += driver->getName();
 
373
                        str += "] FPS:";
 
374
                        str += fps;
 
375
                        device->setWindowCaption(str.c_str());
 
376
                }
 
377
        }
 
378
 
 
379
        myLightManager->drop(); // Drop my implicit reference
 
380
        device->drop();
 
381
        return 0;
 
382
}
 
383
 
 
384
/*
 
385
**/
 
386