1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "nel/3d/texture_bump.h"
32 namespace /* anonymous */ {
34 // Map that give the normalization factor for each map from its sharename. This avoid to generate several time the maps to get the normalization factor if a bumpmap is shared by severals CTextureBump instances;
35 struct CNormalizationInfo
38 float NormalizationFactor
;
40 typedef std::map
<std::string
, CNormalizationInfo
> TNameToNI
; // sharename to the normalization factor
41 class CNameToNFStatic
{
45 CNameToNFStatic() : Initialized(true)
54 CNameToNFStatic s_NameToNF
;
56 } /* anonymous namespace */
58 #define GET_HGT(x, y) ((sint) ((src[(uint) (x) % width + ((uint) (y) % height) * width] & 0x00ff00) >> 8))
59 /// create a DsDt texture from a height map (red component of a rgba bitmap)
60 static void BuildDsDt(uint32
*src
, sint width
, sint height
, uint16
*dest
)
62 #define GET_HGT(x, y) ((sint) ((src[(uint) (x) % width + ((uint) (y) % height) * width] & 0x00ff00) >> 8))
64 for (x
= 0; x
< width
; ++x
)
66 for (y
= 0; y
< height
; ++y
)
68 sint off
= x
+ y
* width
;
69 sint16 ds
= (sint16
) (GET_HGT(x
+ 1, y
) - GET_HGT(x
- 1, y
));
70 sint16 dt
= (sint16
) (GET_HGT(x
, y
+ 1) - GET_HGT(x
, y
- 1));
71 dest
[off
] = (uint16
) ((ds
& 0xff) | ((dt
& 0xff) << 8));
77 /// create a rgba gradient texture from a height map (red component of a rgba bitmap)
78 static void BuildDsDtAsRGBA(uint32
*src
, sint width
, sint height
, uint32
*dest
)
80 #define GET_HGT(x, y) ((sint) ((src[(uint) (x) % width + ((uint) (y) % height) * width] & 0x00ff00) >> 8))
82 for (x
= 0; x
< width
; ++x
)
84 for (y
= 0; y
< height
; ++y
)
86 sint off
= x
+ y
* width
;
87 sint16 ds
= (sint16
) (GET_HGT(x
+ 1, y
) - GET_HGT(x
- 1, y
));
88 sint16 dt
= (sint16
) (GET_HGT(x
, y
+ 1) - GET_HGT(x
, y
- 1));
89 dest
[off
] = 0xff000000 | (uint32
) ((ds
+ 0x80) & 0xff) | (uint32
) ((dt
+ 0x80) << 8);
94 /// Normalize a DsDt texture after it has been built, and return the normalization factor
95 static float NormalizeDsDt(uint16
*src
, sint width
, sint height
)
97 const uint size
= width
* height
;
98 uint highestDelta
= 0;
101 for (k
= 0; k
< size
; ++k
)
103 highestDelta
= std::max(highestDelta
, (uint
) ::abs((sint
) (sint8
) (src
[k
] & 255)));
104 highestDelta
= std::max(highestDelta
, (uint
) ::abs((sint
) (sint8
) (src
[k
] >> 8)));
107 if (highestDelta
== 0)
111 float normalizationFactor
= 127.f
/ highestDelta
;
112 for (k
= 0; k
< size
; ++k
)
114 float fdu
= (sint8
) (src
[k
] & 255) * normalizationFactor
;
115 float fdv
= (sint8
) (src
[k
] >> 8) * normalizationFactor
;
116 NLMISC::clamp(fdu
, -128, 127);
117 NLMISC::clamp(fdv
, -128, 127);
118 uint8 du
= (uint8
) (sint8
) fdu
;
119 uint8 dv
= (uint8
) (sint8
) fdv
;
120 src
[k
] = (uint16
) du
| (((uint16
) dv
) << 8);
122 return 1.f
/ normalizationFactor
;
125 static float NormalizeDsDtAsRGBA(uint32
*src
, sint width
, sint height
)
128 const uint size
= width
* height
;
129 uint highestDelta
= 0;
131 /// first, get the highest delta
132 for (k
= 0; k
< size
; ++k
)
134 highestDelta
= std::max(highestDelta
, (uint
) abs((sint8
) ((src
[k
] & 0xff) - 0x80)));
135 highestDelta
= std::max(highestDelta
, (uint
) abs((sint8
) (((src
[k
] >> 8) & 0xff) - 0x80)));
138 if (highestDelta
== 0)
142 float normalizationFactor
= 127.f
/ highestDelta
;
143 for (k
= 0; k
< size
; ++k
)
145 float fdu
= ((sint8
) ((src
[k
] & 255) - 0x80)) * normalizationFactor
;
146 float fdv
= ((sint8
) (((src
[k
] >> 8) & 0xff) - 0x80)) * normalizationFactor
;
147 NLMISC::clamp(fdu
, -128, 127);
148 NLMISC::clamp(fdv
, -128, 127);
149 uint8 du
= (uint8
) ((sint8
) fdu
+ 0x80);
150 uint8 dv
= (uint8
) ((sint8
) fdv
+ 0x80);
151 src
[k
] = (src
[k
] & 0xffff0000) | (uint32
) du
| (uint32
) (((uint16
) dv
) << 8);
153 return 1.f
/ normalizationFactor
;
164 CTextureBump::CTextureBump() : _NormalizationFactor(NULL
),
165 _DisableSharing(false),
166 _ForceNormalize(true)
168 // mipmapping not supported for now, disable it
169 ITexture::setFilterMode(ITexture::Linear
, ITexture::LinearMipMapOff
);
173 ///==============================================================================================
174 void CTextureBump::setFilterMode(TMagFilter magf
, TMinFilter minf
)
176 nlstop
; // set filter mode not allowed with bump textures (not supported by some GPUs)
179 void CTextureBump::setHeightMap(ITexture
*heightMap
)
181 if (heightMap
!= _HeightMap
)
183 _HeightMap
= heightMap
;
189 ///==============================================================================================
190 void CTextureBump::serial(NLMISC::IStream
&f
)
192 /// version 2 : normalization flag
193 sint ver
= f
.serialVersion(3);
195 ITexture
*tex
= NULL
;
198 f
.serialPolyPtr(tex
);
205 f
.serialPolyPtr(tex
);
207 f
.serial(_DisableSharing
);
210 bool oldFlag
= false;
211 f
.serial(oldFlag
); // backaward compatibility : serial the old "forceAbsoluteOffset" flag
215 f
.serial(_ForceNormalize
);
219 ///==============================================================================================
220 void CTextureBump::doGenerate(bool async
)
227 // generate the height map
228 _HeightMap
->generate();
229 if (!_HeightMap
->convertToType(CBitmap::RGBA
))
235 uint width
= _HeightMap
->getWidth();
236 uint height
= _HeightMap
->getHeight();
237 if (getUploadFormat() == RGBA8888
)
239 CBitmap::resize(_HeightMap
->getWidth(), _HeightMap
->getHeight(), CBitmap::RGBA
);
243 CBitmap::resize(_HeightMap
->getWidth(), _HeightMap
->getHeight(), CBitmap::DsDt
);
245 // build the DsDt map
246 if (getUploadFormat() == RGBA8888
)
248 BuildDsDtAsRGBA((uint32
*) &(_HeightMap
->getPixels()[0]), width
, height
, (uint32
*) &(getPixels()[0]));
252 BuildDsDt((uint32
*) &(_HeightMap
->getPixels()[0]), width
, height
, (uint16
*) &(getPixels()[0]));
255 float normalizationFactor
= 1.0f
;
256 // Normalize the map if needed
259 if (getUploadFormat() == RGBA8888
)
261 normalizationFactor
= NormalizeDsDtAsRGBA((uint32
*) &(getPixels()[0]), width
, height
);
265 normalizationFactor
= NormalizeDsDt((uint16
*) &(getPixels()[0]), width
, height
);
268 // create entry in the map for the normalization factor
269 std::string shareName
= getShareName();
270 nlassertverbose(s_NameToNF
.Initialized
);
271 TNameToNI::iterator it
= s_NameToNF
.Map
.find(shareName
);
272 if (it
== s_NameToNF
.Map
.end())
274 // create a new entry
275 CNormalizationInfo ni
;
277 ni
.NormalizationFactor
= normalizationFactor
;
278 std::pair
<TNameToNI::iterator
, bool> pb
= s_NameToNF
.Map
.insert(TNameToNI::value_type(shareName
, ni
));
279 _NormalizationFactor
= &(pb
.first
->second
.NormalizationFactor
);
283 // another map has computed the factor
284 _NormalizationFactor
= &(it
->second
.NormalizationFactor
);
285 ++(it
->second
.NumRefs
);
288 if (_HeightMap
->getReleasable())
290 _HeightMap
->release();
294 ///==============================================================================================
295 void CTextureBump::release()
298 if (_HeightMap
!= NULL
)
300 if (_HeightMap
->getReleasable())
302 _HeightMap
->release();
308 ///==============================================================================================
309 bool CTextureBump::supportSharing() const
311 return !_DisableSharing
&& _HeightMap
&& _HeightMap
->supportSharing();
315 ///==============================================================================================
316 std::string
CTextureBump::getShareName() const
318 nlassert(supportSharing());
319 return "BumpDsDt:" + _HeightMap
->getShareName();
322 ///==============================================================================================
323 float CTextureBump::getNormalizationFactor()
327 if (_NormalizationFactor
) return *_NormalizationFactor
;
329 if (!_HeightMap
) return 1.f
;
330 // not computed yet, see if another map has computed it
331 std::string shareName
= getShareName();
332 nlassertverbose(s_NameToNF
.Initialized
);
333 TNameToNI::iterator it
= s_NameToNF
.Map
.find(shareName
);
334 if (it
!= s_NameToNF
.Map
.end())
336 _NormalizationFactor
= &(it
->second
.NormalizationFactor
);
337 ++(it
->second
.NumRefs
);
342 if (this->getReleasable()) this->release();
344 return _NormalizationFactor
? *_NormalizationFactor
: 1.f
;
347 ///==============================================================================================
348 CTextureBump::~CTextureBump()
350 if (s_NameToNF
.Initialized
&& _NormalizationFactor
&& !s_NameToNF
.Map
.empty())
352 // find normalization factor from its name
353 TNameToNI::iterator it
= s_NameToNF
.Map
.find(getShareName());
356 if (it
!= s_NameToNF
.Map
.end())
358 // we can delete it only if it's not used anymore
359 if (--(it
->second
.NumRefs
) == 0) s_NameToNF
.Map
.erase(it
);