diff options
Diffstat (limited to 'libraries/irrlicht-1.8/examples/20.ManagedLights/main.cpp')
-rw-r--r-- | libraries/irrlicht-1.8/examples/20.ManagedLights/main.cpp | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/libraries/irrlicht-1.8/examples/20.ManagedLights/main.cpp b/libraries/irrlicht-1.8/examples/20.ManagedLights/main.cpp new file mode 100644 index 0000000..f67337a --- /dev/null +++ b/libraries/irrlicht-1.8/examples/20.ManagedLights/main.cpp | |||
@@ -0,0 +1,386 @@ | |||
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 | 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 | 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 | 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 | |||