1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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"
30 #include "sky_render.h"
31 #include "sky_material_setup.h"
32 #include "light_cycle_manager.h"
39 H_AUTO_DECL(RZ_SkyRender
)
42 using namespace NLMISC
;
44 UScene
*SkyScene
= NULL
;
46 UInstance Sky2ndPass
= NULL
;
47 NL3D::UInstance SkyFogPart
= NULL
;
54 H_AUTO_DECL ( RZ_Client_Render_Sky
)
56 extern UDriver
*Driver
;
60 //===================================================================================================
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 //===================================================================================================
75 if (!SkyScene
) return;
76 Driver
->deleteScene(SkyScene
);
80 //===================================================================================================
81 static void applySkyMaterialSetup(UInstance instance
, bool isNight
, uint stage
, bool skipFirstMaterial
= false)
83 H_AUTO_USE(RZ_SkyRender
)
87 // Return if there is no continent selected.
88 if(ContinentMngr
.cur() == 0)
92 ContinentMngr
.cur()->DaySkySetup
.applyToInstance(instance
, stage
, skipFirstMaterial
);
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;
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
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);
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);
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);
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
267 // bg for setup 0 +-----+ ---> background depending on hour (day, dusk, night)
270 // bg for setup 1 +-----+
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
;
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
;
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
;
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())
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
);
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
);
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);
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
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);
368 showSkyParts(false, true, false);
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);
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
);
390 // release textures for second pass if it is not needed anymore
391 if (!twoPassDone
&& secondPassUsed
)
393 removeInstanceTextures(Sky2ndPass
);
394 secondPassUsed
= false;