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/>.
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"
33 using namespace NLMISC
;
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
)
47 Map
= buildSharedBitmap(ci
.MapName
, bitmapByName
, builtBitmaps
, alreadyBuilt
);
50 // *************************************************************************************************
51 CRGBA
CSkyObject::CColorInfo::computeColor(float dayPart
, float weatherLevel
, CRGBA fogColor
)
55 case Unused
: return CRGBA(0, 0, 0, 0);
56 case FogColor
: return fogColor
;
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
);
73 nlassert(0); // unknwon type
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
)
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
107 // rotate the bitmap because it is faster to blit a row than a column
108 WeatherToGradient
[k
]->rot90CCW();
109 WeatherToGradient
[k
]->flipV();
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;
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
))
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);
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
),
213 gradientCacheBlurred
.getHeight(),
214 gradientCacheBlurred
.getWidth());
215 // clamp on v coordinate
216 instance
.getMaterial(k
).setWrapT(TargetTextureStage
, NL3D::UInstanceMaterial::Clamp
);
221 // *************************************************************************************************
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
,
234 if (instance
.empty()) return;
236 // set display priority
237 instance
.setTransparencyPriority(sheet
.TransparencyPriority
);
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
);
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())
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;
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
290 PS
.setUserColor(newDiffuseColor
);
291 // PS are hiden / shown, so must unfreeze hrc. (adding an isntance group causes hrc to be frozen)
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
);
303 Instance
.getMaterial(k
).setDiffuse(newDiffuseColor
);
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)
323 // is it a particle system
324 if (ParticleEmitters
.Mode
!= Unused
)
326 CRGBA newParticleEmittersColor
= ParticleEmitters
.computeColor(dayPart
, weatherLevel
, fogColor
);
327 if (newParticleEmittersColor
!= LastParticleEmittersColor
)
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
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)
371 if (envMapScene
&& !VisibleInEnvMap
)
375 else if (!envMapScene
&& !VisibleInMainScene
)
382 Instance
.unfreezeHRC();
387 Instance
.freezeHRC();
389 double animTime
= animationDate
.Hour
+ (double) animationDate
.Day
* (double) numHoursInDay
;
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
;
409 double v
= TexPanner
[k
].V
* animTime
;
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
;
423 for(uint l
= 0; l
< numMaterials
; ++l
)
425 Instance
.getMaterial(l
).enableUserTexMat(k
);
426 Instance
.getMaterial(l
).setUserTexMat(k
, mat
);
434 for(uint k
= 0; k
< SKY_MAX_NUM_FX_USER_PARAMS
; ++k
)
438 CRGBA color
= FXUserParams
[k
]->getColor(dayPart
, weatherLevel
, true, false);
439 PS
.setUserParam(k
, color
.R
/ 255.f
);
446 // *************************************************************************************************
447 CSkyObject::~CSkyObject()