Linux multi-monitor fullscreen support
[ryzomcore.git] / ryzom / client / src / sky_render.cpp
blobe917d368062569ecfd03250b0be1d78ef2005ecb
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "stdpch.h"
21 #include "nel/3d/u_instance_material.h"
22 #include "nel/3d/u_camera.h"
23 #include "nel/3d/u_scene.h"
24 #include "nel/3d/u_instance.h"
26 #include "world_database_manager.h"
27 #include "continent_manager.h"
28 #include "weather_manager_client.h"
29 #include "weather.h"
30 #include "sky_render.h"
31 #include "sky_material_setup.h"
32 #include "light_cycle_manager.h"
33 #include "global.h"
35 #ifdef DEBUG_NEW
36 #define new DEBUG_NEW
37 #endif
39 H_AUTO_DECL(RZ_SkyRender)
41 using namespace NL3D;
42 using namespace NLMISC;
44 UScene *SkyScene = NULL;
45 UInstance Sky = NULL;
46 UInstance Sky2ndPass = NULL;
47 NL3D::UInstance SkyFogPart = NULL;
49 /////////////
50 // GLOBALS //
51 /////////////
53 // Hierarchical timer
54 H_AUTO_DECL ( RZ_Client_Render_Sky )
56 extern UDriver *Driver;
57 extern UScene *Scene;
60 //===================================================================================================
61 void createSkyScene()
63 if (SkyScene) return;
64 if (!Scene) return;
65 // Create the scene for the sky.
66 SkyScene = Driver->createScene(true);
67 // enable Scene Lighting
68 SkyScene->enableLightingSystem(true);
69 SkyScene->setAmbientGlobal(CRGBA::Black);
72 //===================================================================================================
73 void deleteSkyScene()
75 if (!SkyScene) return;
76 Driver->deleteScene(SkyScene);
77 SkyScene = NULL;
80 //===================================================================================================
81 static void applySkyMaterialSetup(UInstance instance, bool isNight, uint stage, bool skipFirstMaterial = false)
83 H_AUTO_USE(RZ_SkyRender)
84 if(instance.empty())
85 return;
87 // Return if there is no continent selected.
88 if(ContinentMngr.cur() == 0)
89 return;
91 if(!isNight)
92 ContinentMngr.cur()->DaySkySetup.applyToInstance(instance, stage, skipFirstMaterial);
93 else
94 ContinentMngr.cur()->NightSkySetup.applyToInstance(instance, stage, skipFirstMaterial);
97 //===================================================================================================
98 static void applySkyTex(UInstance instance, uint stage, const std::string &name)
100 H_AUTO_USE(RZ_SkyRender)
101 if (instance.empty()) return;
102 if (instance.getNumMaterials() < 1) return;
103 UInstanceMaterial im = instance.getMaterial(0);
104 if (im.getLastTextureStage() >= (sint) stage)
106 if (NLMISC::nlstricmp(im.getTextureFileName(stage), name) != 0)
108 im.setTextureFileName(name, stage);
112 //===================================================================================================
113 /** Set alpha factors for a sky dome.
114 * the constantAlpha gives the blend between 2 sky texture for the instance
115 * the diffuseAlpha gives a modulate factor for the sky, which is then blended additively
117 static void setSkyAlpha(UInstance instance, float constantAlpha, float diffuseAlpha, bool opaque)
119 H_AUTO_USE(RZ_SkyRender)
120 if (instance.empty()) return;
121 CRGBA constantCol(255, 255, 255, (uint8) (constantAlpha * 255.f));
122 CRGBA diffuseCol(255, 255, 255, (uint8) (diffuseAlpha * 255.f));
124 uint numMat = instance.getNumMaterials();
125 if (numMat == 0) return;
126 for(uint k = 0; k < numMat; ++k)
128 instance.getMaterial(k).setConstantColor(1, constantCol);
129 instance.getMaterial(k).setColor(diffuseCol);
132 // set the first material to be opaque
133 instance.getMaterial(0).setDstBlend(opaque ? UInstanceMaterial::zero : UInstanceMaterial::one);
137 //===================================================================================================
138 static inline void showSkyPart(UInstance i, bool show)
140 H_AUTO_USE(RZ_SkyRender)
141 if (i.empty()) return;
142 if (show) i.show();
143 else i.hide();
146 //===================================================================================================
147 static void showSkyParts(bool firstPass, bool secondPass, bool fogPart)
149 H_AUTO_USE(RZ_SkyRender)
150 showSkyPart(Sky, firstPass);
151 showSkyPart(Sky2ndPass, secondPass);
152 showSkyPart(SkyFogPart, fogPart);
155 //===================================================================================================
156 static void renderStandardSky(float dayNight)
158 H_AUTO_USE(RZ_SkyRender)
159 showSkyParts(true, false, false);
160 // normal setup : stage 0 is day and stage 1 is night, 1 pass is required
161 applySkyMaterialSetup(Sky, false, 0); // duplicate current setup in both stages
162 applySkyMaterialSetup(Sky, true, 1);
163 setSkyAlpha(Sky, dayNight, 1.f, true); // replace mode
164 SkyScene->render();
167 //===================================================================================================
168 /** setup a sky dome for a blend between night & day, with eventually a weather blend
170 static void setupSkyDomeTextures(UInstance instance, const std::string &weatherDay, const std::string &weatherNight)
172 H_AUTO_USE(RZ_SkyRender)
173 if (instance.empty()) return;
174 // setup stage 0 (day)
175 if (weatherDay.empty())
177 applySkyMaterialSetup(instance, false, 0);
179 else
181 applySkyMaterialSetup(instance, false, 0, true);
182 applySkyTex(instance, 0, weatherDay);
184 // setup stage 1 (night)
185 if (weatherNight.empty())
187 applySkyMaterialSetup(instance, true, 1);
189 else
191 applySkyMaterialSetup(instance, true, 1, true);
192 applySkyTex(instance, 1, weatherNight);
196 //===================================================================================================
197 /** Set constant color for all material of an instance
199 static void setInstanceConstantColor(UInstance instance, CRGBA color)
201 H_AUTO_USE(RZ_SkyRender)
202 if (instance.empty()) return;
203 uint numMat = instance.getNumMaterials();
204 for(uint k = 0; k < numMat; ++k)
206 instance.getMaterial(k).setConstantColor(0, color);
210 //===================================================================================================
211 /** remove all textures from an instance (to free memory..)
213 static void removeInstanceTextures(UInstance instance)
215 H_AUTO_USE(RZ_SkyRender)
216 if (instance.empty()) return;
217 uint numMat = instance.getNumMaterials();
218 for(uint k = 0; k < numMat; ++k)
220 sint numStages = instance.getMaterial(k).getLastTextureStage() + 1;
221 for(sint l = 0; l < numStages; ++l)
223 if (instance.getMaterial(k).isTextureFile((uint) l))
225 instance.getMaterial(k).setTextureFileName("", (uint) l);
232 //===================================================================================================
233 void renderSky(const CLightCycleManager &lcm, NLMISC::CRGBA fogColor)
235 H_AUTO_USE(RZ_SkyRender)
236 H_AUTO_USE ( RZ_Client_Render_Sky )
238 static bool secondPassUsed = true; // tells if a second pass was needed at the previous rendering
239 if (!SkyScene || MainCam.empty() || !Scene) return;
241 float lightLevel = lcm.getLightLevel();
242 float duskRatio = lcm.getLightDesc().DuskRatio;
244 Driver->enableFog(false);
245 // Render the Sky.
246 CFrustum frust = MainCam.getFrustum();
247 UCamera camSky = SkyScene->getCam();
248 camSky.setTransformMode(UTransform::DirectMatrix);
249 // must have our own Far!!!
250 frust.Far= SkyCameraZFar;
251 camSky.setFrustum(frust);
252 CMatrix skyCameraMatrix;
253 skyCameraMatrix.identity();
254 skyCameraMatrix= MainCam.getMatrix();
255 skyCameraMatrix.setPos(CVector::Null);
256 camSky.setMatrix(skyCameraMatrix);
257 SkyScene->setViewport(Scene->getViewport());
258 bool isNight = lightLevel > 0.5f;
259 if (Sky.empty()) return;
260 // See in the weather setup if there is a weather related skyDome
261 const CWeatherState &ws = WeatherManager.getCurrWeatherState();
263 bool twoPassDone = false;
265 // build a set of 4 background between which to blend depending on weather & hour
266 // 00 01
267 // bg for setup 0 +-----+ ---> background depending on hour (day, dusk, night)
268 // | |
269 // | |
270 // bg for setup 1 +-----+
271 // 10 11
272 const std::string *bg00, *bg01, *bg10, *bg11;
273 float blendFactor; // blendFactor for time
274 switch (lcm.getState())
276 case CLightCycleManager::DayToNight:
277 if (lightLevel <= duskRatio)
279 blendFactor = duskRatio != 0 ? lightLevel / duskRatio : 0.f;
280 bg00 = &ws.DayBackgroundFileName1;
281 bg01 = &ws.DuskBackgroundFileName1;
282 bg10 = &ws.DayBackgroundFileName2;
283 bg11 = &ws.DuskBackgroundFileName2;
285 else
287 blendFactor = duskRatio != 1.f ? (lightLevel - duskRatio) / (1.f - duskRatio) : 0.f;
288 bg00 = &ws.DuskBackgroundFileName1;
289 bg01 = &ws.NightBackgroundFileName1;
290 bg10 = &ws.DuskBackgroundFileName2;
291 bg11 = &ws.NightBackgroundFileName2;
293 break;
294 default: // not a day->night transition, so no 'dusk' step.
295 blendFactor = lightLevel;
296 bg00 = &ws.DayBackgroundFileName1;
297 bg01 = &ws.NightBackgroundFileName1;
298 bg10 = &ws.DayBackgroundFileName2;
299 bg11 = &ws.NightBackgroundFileName2;
300 break;
303 // Do not draw the sky in indoor continents
304 if (!ContinentMngr.cur()->Indoor)
306 if (lightLevel == 0.f || lightLevel == 1.f)
308 const std::string &bg1 = isNight ? *bg01 : *bg00;
309 const std::string &bg2 = isNight ? *bg11 : *bg10;
310 if (!bg1.empty() || !bg2.empty())
313 if (!bg1.empty())
315 if (!bg2.empty()) // transition between 2 weather sky domes ?
317 applySkyMaterialSetup(Sky, isNight, 0, true); // duplicate current setup in both stages
318 applySkyMaterialSetup(Sky, isNight, 1, true);
319 // override sky dome texture with current texture
320 applySkyTex(Sky, 0, bg1);
321 applySkyTex(Sky, 1, bg2);
324 else
326 // transition from bad weather sky to normal sky
327 applySkyMaterialSetup(Sky, isNight, 0, true); // duplicate current setup in both stages
328 applySkyMaterialSetup(Sky, isNight, 1);
329 applySkyTex(Sky, 0, bg1);
332 else
334 // transition from normal weather to bad weather
335 applySkyMaterialSetup(Sky, isNight, 0); // duplicate current setup in both stages
336 applySkyMaterialSetup(Sky, isNight, 1, true);
337 applySkyTex(Sky, 1, bg2);
339 showSkyParts(true, false, false);
340 setSkyAlpha(Sky, ws.BlendFactor, 1.f, true);
341 SkyScene->render();
343 else
345 renderStandardSky(blendFactor);
348 else // We are in a transition between day and night
350 // if there's one weather texture, multipass rendering is needed
351 if ( bg00->empty()
352 && bg01->empty()
353 && bg10->empty()
354 && bg11->empty())
356 // no weather texture
357 renderStandardSky(blendFactor);
359 else // 2 pass rendering
361 // setup textures of the skydomes
362 setupSkyDomeTextures(Sky, *bg00, *bg01);
363 setupSkyDomeTextures(Sky2ndPass, *bg10, *bg11);
364 setSkyAlpha(Sky, blendFactor, 1.f - ws.BlendFactor, true /* opaque */);
365 setSkyAlpha(Sky2ndPass, blendFactor, ws.BlendFactor, false /* alpha additif */);
366 showSkyParts(true, false, false);
367 SkyScene->render();
368 showSkyParts(false, true, false);
369 SkyScene->render();
370 twoPassDone = true;
371 secondPassUsed = true;
374 // Blend the fog part
375 if (!SkyFogPart.empty())
377 showSkyParts(false, false, true);
378 setInstanceConstantColor(SkyFogPart, fogColor);
379 // set texture matrix for first material to get the right height for fog
380 if (SkyFogPart.getNumMaterials() > 0)
382 UInstanceMaterial im = SkyFogPart.getMaterial(0);
383 im.enableUserTexMat(0);
384 CMatrix uvMat;
385 uvMat.scale(CVector(1.f, ws.FogGradientFactor != 0.f ? 1.f / ws.FogGradientFactor : 0.f, 1.f)); // scale the fog part
386 im.setUserTexMat(0, uvMat);
388 SkyScene->render();
390 // release textures for second pass if it is not needed anymore
391 if (!twoPassDone && secondPassUsed)
393 removeInstanceTextures(Sky2ndPass);
394 secondPassUsed = false;