Linux multi-monitor fullscreen support
[ryzomcore.git] / ryzom / client / src / sky_object.cpp
blob94185ef93af359ec6c96b2ad247c36e83ed6129e
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/>.
17 #include "stdpch.h"
19 #include "nel/3d/u_instance_material.h"
20 #include "nel/3d/u_particle_system_instance.h"
21 #include "nel/3d/u_instance.h"
23 #include "nel/misc/path.h"
24 #include "nel/misc/file.h"
26 #include "sky_object.h"
27 #include "sky.h"
29 #ifdef DEBUG_NEW
30 #define new DEBUG_NEW
31 #endif
33 using namespace NLMISC;
34 using namespace NL3D;
36 ////////////////////////////
37 // CSkyObject::CColorInfo //
38 ////////////////////////////
40 // *************************************************************************************************
41 void CSkyObject::CColorInfo::init(const CSkyObjectSheet::CColorInfoSheet &ci,
42 std::map<std::string, CBitmap *> &bitmapByName,
43 std::vector<CBitmap *> &builtBitmaps)
45 Mode = ci.Mode;
46 bool alreadyBuilt;
47 Map = buildSharedBitmap(ci.MapName, bitmapByName, builtBitmaps, alreadyBuilt);
50 // *************************************************************************************************
51 CRGBA CSkyObject::CColorInfo::computeColor(float dayPart, float weatherLevel, CRGBA fogColor)
53 switch(Mode)
55 case Unused: return CRGBA(0, 0, 0, 0);
56 case FogColor: return fogColor;
57 case BitmapColor:
58 clamp(dayPart, 0.f, 1.f);
59 clamp(weatherLevel, 0.f, 1.f);
60 if (!Map) return CRGBA(0, 0, 0, 0);
61 return Map->getColor(dayPart, weatherLevel, true, false);
62 case BitmapColorModulatedByFogColor:
64 clamp(dayPart, 0.f, 1.f);
65 clamp(weatherLevel, 0.f, 1.f);
66 if (!Map) return CRGBA(0, 0, 0, 0);
67 CRGBA result = Map->getColor(dayPart, weatherLevel, true, false);
68 result.modulateFromColor(result, fogColor);
69 return result;
71 break;
72 default:
73 nlassert(0); // unknwon type
74 break;
76 return CRGBA(0, 0, 0, 0);
80 // *************************************************************************************************
81 ////////////////////////////////////
82 // CSkyObject::CColorGradientInfo //
83 ////////////////////////////////////
85 void CSkyObject::CColorGradientInfo::init(const CSkyObjectSheet::CColorGradientInfoSheet &cgis,
86 std::map<std::string,CBitmap *> &bitmapByName,
87 std::vector<CBitmap *> &builtBitmaps)
89 TargetTextureStage = cgis.TargetTextureStage;
90 WeatherToGradient.resize(cgis.WeatherToGradient.size());
91 CBitmap *lastSeenBitmap = NULL;
92 for(uint k = 0; k < cgis.WeatherToGradient.size(); ++k)
94 bool alreadyBuilt;
95 WeatherToGradient[k] = buildSharedBitmap(cgis.WeatherToGradient[k], bitmapByName, builtBitmaps, alreadyBuilt);
96 if (WeatherToGradient[k])
98 if (!WeatherToGradient[k]->convertToType(CBitmap::RGBA))
100 // can't use bitmap..
101 WeatherToGradient[k] = NULL; // don't do a delete here because it'is 'builtBitmaps' that has ownership
103 else
105 if (!alreadyBuilt)
107 // rotate the bitmap because it is faster to blit a row than a column
108 WeatherToGradient[k]->rot90CCW();
109 WeatherToGradient[k]->flipV();
111 if (lastSeenBitmap)
113 if (WeatherToGradient[k]->getWidth() != lastSeenBitmap->getWidth() ||
114 WeatherToGradient[k]->getHeight() != lastSeenBitmap->getHeight()
117 nlwarning("All bitmaps must have the same size in the gradient");
120 lastSeenBitmap = WeatherToGradient[k];
127 static void dumpGrad(CBitmap &bm)
129 CRGBA *pixs = (CRGBA *) &bm.getPixels(0)[0];
130 for(uint k = 0; k < bm.getWidth(); ++k)
132 nlinfo("(r, g, b, a) = (%d, %d, %d, %d)", pixs[k].R, pixs[k].G, pixs[k].B, pixs[k].A);
138 // *************************************************************************************************
139 void CSkyObject::CColorGradientInfo::setup(NL3D::UInstance instance, float dayPart, float weatherLevel, CBitmap &gradientCache, CBitmap &gradientCacheBlurred)
141 if (instance.empty()) return;
142 if (WeatherToGradient.empty()) return;
143 clamp(dayPart, 0.f, 1.f);
144 clamp(weatherLevel, 0.f, 1.f);
145 // takes 2 closest bitmaps to do the blend
146 uint bm0Index = std::min(uint(weatherLevel * WeatherToGradient.size()), uint(WeatherToGradient.size() - 1));
147 uint bm1Index = std::min(uint(weatherLevel * WeatherToGradient.size() + 1), uint(WeatherToGradient.size() - 1));
148 CBitmap *bm0 = WeatherToGradient[bm0Index];
149 CBitmap *bm1 = WeatherToGradient[bm1Index];
150 if (!bm1 && !bm0) return;
151 if (!bm1) bm1 = bm0;
152 if (!bm0) bm0 = bm1;
153 // make sure that both bitmap have the same size
154 if (bm0->getWidth() != bm1->getWidth() || bm0->getHeight() != bm1->getHeight()) return;
155 // extract the 2 slices before to blend
156 uint slice0 = (uint) (dayPart * bm0->getHeight());
157 uint slice1 = (uint) (dayPart * bm0->getHeight() + 1) % bm0->getHeight();
158 //nlinfo("slice0 = %d", slice0);
159 Slice0[0].resize(bm0->getWidth(), 1);
160 Slice0[1].resize(bm0->getWidth(), 1);
161 Slice0[0].blit(*bm0, 0, slice0, bm0->getWidth(), 1, 0, 0);
162 Slice0[1].blit(*bm0, 0, slice1, bm0->getWidth(), 1, 0, 0);
163 Slice0[0].blend(Slice0[0], Slice0[1], (uint) (256 * fmodf(dayPart * bm0->getHeight(), 1.f)), true);
164 Slice1[0].resize(bm0->getWidth(), 1);
165 Slice1[1].resize(bm0->getWidth(), 1);
166 Slice1[0].blit(*bm1, 0, slice0, bm1->getWidth(), 1, 0, 0);
167 Slice1[1].blit(*bm1, 0, slice1, bm1->getWidth(), 1, 0, 0);
168 Slice1[0].blend(Slice1[0], Slice1[1], (uint) (256 * fmodf(dayPart * bm0->getHeight(), 1.f)), true);
169 Slice0[0].blend(Slice0[0], Slice1[0], (uint) (256 * fmodf(weatherLevel * WeatherToGradient.size(), 1.f)), true);
170 // see if blended result differs from cache, if so, update texture of the instance
171 const uint32 *newGrad = (uint32 *) &Slice0[0].getPixels()[0];
172 if (gradientCache.getPixels(0).empty())
174 gradientCache.resize(bm1->getWidth() , 1, CBitmap::RGBA);
176 const uint32 *oldGrad = (uint32 *) &gradientCache.getPixels()[0];
177 nlassert(gradientCache.getWidth() == Slice0[0].getWidth());
178 if (!std::equal(newGrad, newGrad + bm0->getWidth(), oldGrad))
180 // update the cache
181 gradientCache.swap(Slice0[0]);
182 // build a blurred version of the gradient cache (improve quality of gradient)
183 if (gradientCacheBlurred.getWidth() != gradientCache.getWidth() ||
184 gradientCacheBlurred.getHeight() != gradientCache.getHeight())
186 gradientCacheBlurred.resize(gradientCache.getWidth(), 1);
188 nlassert(gradientCacheBlurred.PixelFormat == gradientCache.PixelFormat);
189 CRGBA *destPix = (CRGBA *) &gradientCacheBlurred.getPixels()[0];
190 const CRGBA *srcPix = (const CRGBA *) &gradientCache.getPixels(0)[0];
191 *destPix++ = *srcPix ++;
192 const CRGBA *lastSrcPix = srcPix + Slice0[0].getWidth() - 2;
193 while (srcPix != lastSrcPix)
195 destPix->R = (uint8) (((uint16) srcPix[- 1].R + (uint16) srcPix->R + (uint16) srcPix[1].R) * (256 / 3) >> 8);
196 destPix->G = (uint8) (((uint16) srcPix[- 1].G + (uint16) srcPix->G + (uint16) srcPix[1].G) * (256 / 3) >> 8);
197 destPix->B = (uint8) (((uint16) srcPix[- 1].B + (uint16) srcPix->B + (uint16) srcPix[1].B) * (256 / 3) >> 8);
198 destPix->A = (uint8) (((uint16) srcPix[- 1].A + (uint16) srcPix->A + (uint16) srcPix[1].A) * (256 / 3) >> 8);
199 ++ destPix;
200 ++ srcPix;
202 *destPix++ = *srcPix ++;
203 // set the new texture
204 uint numMaterials = instance.getNumMaterials();
205 for(uint k = 0; k < numMaterials; ++k)
207 // do a free ccw rotate by swapping height & width (because height is 1)
208 instance.getMaterial(k).setTextureMem(TargetTextureStage,
209 &gradientCacheBlurred.getPixels()[0],
210 gradientCacheBlurred.getWidth() * gradientCacheBlurred.getHeight() * sizeof(uint32),
211 false,
212 false,
213 gradientCacheBlurred.getHeight(),
214 gradientCacheBlurred.getWidth());
215 // clamp on v coordinate
216 instance.getMaterial(k).setWrapT(TargetTextureStage, NL3D::UInstanceMaterial::Clamp);
221 // *************************************************************************************************
222 ////////////////
223 // CSkyObject //
224 ////////////////
226 void CSkyObject::init(const CSkyObjectSheet::CVersionSheet &sheet,
227 NL3D::UInstance instance,
228 std::map<std::string,CBitmap *> &bitmapByName,
229 std::vector<CBitmap *> &builtBitmaps,
230 bool visibleInMainScene,
231 bool visibleInEnvMap
234 if (instance.empty()) return;
235 Instance = instance;
236 // set display priority
237 instance.setTransparencyPriority(sheet.TransparencyPriority);
238 PS.cast(Instance);
240 DiffuseColor.init(sheet.DiffuseColor, bitmapByName, builtBitmaps);
241 ParticleEmitters.init(sheet.ParticleEmitters, bitmapByName, builtBitmaps);
242 for(uint k = 0; k < SKY_MAX_NUM_STAGE; ++k)
244 ConstantColor[k].init(sheet.ConstantColor[k], bitmapByName, builtBitmaps);
245 bool alreadyBuilt;
246 if (!sheet.OffsetUBitmap[k].empty())
248 OffsetUBitmap[k] = buildSharedBitmap(sheet.OffsetUBitmap[k], bitmapByName, builtBitmaps, alreadyBuilt);
250 if (!sheet.OffsetVBitmap[k].empty())
252 OffsetVBitmap[k] = buildSharedBitmap(sheet.OffsetVBitmap[k], bitmapByName, builtBitmaps, alreadyBuilt);
255 ColorGradient.init(sheet.ColorGradient, bitmapByName, builtBitmaps);
256 RefColor = sheet.RefColor;
257 std::copy(sheet.TexPanner, sheet.TexPanner + SKY_MAX_NUM_STAGE, TexPanner);
258 std::copy(sheet.OffsetFactor, sheet.OffsetFactor + SKY_MAX_NUM_STAGE, OffsetFactor);
259 for(uint k = 0; k < SKY_MAX_NUM_FX_USER_PARAMS; ++k)
261 if (!sheet.FXUserParamBitmap[k].empty())
263 bool alreadyBuilt;
264 FXUserParams[k] = buildSharedBitmap(sheet.FXUserParamBitmap[k], bitmapByName, builtBitmaps, alreadyBuilt);
267 Name = sheet.ShapeName;
268 VisibleInMainScene = visibleInMainScene;
269 VisibleInEnvMap = visibleInEnvMap;
273 // *************************************************************************************************
274 bool CSkyObject::setup(const CClientDate &date, const CClientDate &animationDate, float numHoursInDay, float weatherLevel, CRGBA fogColor, bool envMapScene)
276 if (Instance.empty()) return false;
277 Active = true;
278 nlassert(numHoursInDay > 0.f);
279 float dayPart = date.Hour / numHoursInDay;
280 clamp(dayPart, 0.f, 1.f);
281 clamp(weatherLevel, 0.f, 1.f);
282 if (DiffuseColor.Mode != Unused)
284 CRGBA newDiffuseColor = DiffuseColor.computeColor(dayPart, weatherLevel, fogColor);
285 if (newDiffuseColor != LastDiffuseColor)
287 // is it a particle system
288 if (!PS.empty())
290 PS.setUserColor(newDiffuseColor);
291 // PS are hiden / shown, so must unfreeze hrc. (adding an isntance group causes hrc to be frozen)
292 PS.unfreezeHRC();
294 else
296 // set diffuse color for each material with normal shader
297 uint numMaterials = Instance.getNumMaterials();
298 for(uint k = 0; k < numMaterials; ++k)
300 UInstanceMaterial im = Instance.getMaterial(k);
301 if (im.isLighted())
303 Instance.getMaterial(k).setDiffuse(newDiffuseColor);
305 else
307 Instance.getMaterial(k).setColor(newDiffuseColor);
310 // set mean color for other objects (lens flares...)
311 Instance.setMeanColor(newDiffuseColor);
313 LastDiffuseColor = newDiffuseColor;
315 if (RefColor == DiffuseColorRef) // if this is the ref color, then the object is not visible if alpha is 0
317 if (newDiffuseColor.A == 0)
319 Active = false;
323 // is it a particle system
324 if (ParticleEmitters.Mode != Unused)
326 CRGBA newParticleEmittersColor = ParticleEmitters.computeColor(dayPart, weatherLevel, fogColor);
327 if (newParticleEmittersColor != LastParticleEmittersColor)
329 if (!PS.empty())
331 // emitters are on is any of the components is not 0
332 PS.activateEmitters(newParticleEmittersColor != CRGBA::Black);
334 LastParticleEmittersColor = newParticleEmittersColor;
336 if (RefColor == ParticleEmittersColorRef) // if this is the ref color, then the object is not visible if alpha is 0
338 if (LastParticleEmittersColor == CRGBA::Black)
340 if (!PS.hasParticles()) // can deactivate PS only when all particles are off
342 Active = false;
347 uint numMaterials = Instance.getNumMaterials();
348 for(uint k = 0; k < SKY_MAX_NUM_STAGE; ++k)
350 if (ConstantColor[k].Mode != Unused)
352 CRGBA newConstantColor = ConstantColor[k].computeColor(dayPart, weatherLevel, fogColor);
353 if (newConstantColor != LastConstantColor[k])
355 for(uint l = 0; l < numMaterials; ++l)
357 Instance.getMaterial(l).setConstantColor(k, newConstantColor);
359 LastConstantColor[k] = newConstantColor;
361 if (RefColor == (TSkyRefColor) (ConstantColor0Ref + k))
363 if (newConstantColor.A == 0)
365 Active = false;
370 bool draw = Active;
371 if (envMapScene && !VisibleInEnvMap)
373 draw = false;
375 else if (!envMapScene && !VisibleInMainScene)
377 draw = false;
379 if (draw)
381 Instance.show();
382 Instance.unfreezeHRC();
384 else
386 Instance.hide();
387 Instance.freezeHRC();
389 double animTime = animationDate.Hour + (double) animationDate.Day * (double) numHoursInDay;
390 if (PS.empty())
392 ////////////////////
393 // gradient setup //
394 ////////////////////
395 ColorGradient.setup(Instance, dayPart, weatherLevel, GradientCache, GradientCacheBlurred);
397 ///////////////////////
398 // tex panning setup //
399 ///////////////////////
400 for(uint k = 0; k < SKY_MAX_NUM_STAGE; ++k)
402 if (TexPanner[k].U != 0.f || TexPanner[k].V != 0.f ||
403 OffsetUBitmap[k] != NULL || OffsetVBitmap[k] != NULL )
405 //nlinfo("global date = %f", animTime);
406 // there's tex panning for that stage
407 double u = TexPanner[k].U * animTime;
408 u = fmod(u, 1);
409 double v = TexPanner[k].V * animTime;
410 v = fmod(v, 1);
411 CVector offset((float) u, (float) v, 0.f);
412 // apply scaling if needed
413 if (OffsetUBitmap[k])
415 offset.x += OffsetFactor[k].U * OffsetUBitmap[k]->getColor(dayPart, weatherLevel, true, false).R;
417 if (OffsetVBitmap[k])
419 offset.y += OffsetFactor[k].V * OffsetVBitmap[k]->getColor(dayPart, weatherLevel, true, false).R;
421 CMatrix mat;
422 mat.setPos(offset);
423 for(uint l = 0; l < numMaterials; ++l)
425 Instance.getMaterial(l).enableUserTexMat(k);
426 Instance.getMaterial(l).setUserTexMat(k, mat);
431 else
433 // user params setup
434 for(uint k = 0; k < SKY_MAX_NUM_FX_USER_PARAMS; ++k)
436 if (FXUserParams[k])
438 CRGBA color = FXUserParams[k]->getColor(dayPart, weatherLevel, true, false);
439 PS.setUserParam(k, color.R / 255.f);
443 return Active;
446 // *************************************************************************************************
447 CSkyObject::~CSkyObject()