Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / texture_bump.cpp
blobfc1fe4139635e833dfd3ca77cbbcd36dd66ece89
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
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/>.
20 #include "std3d.h"
22 #include "nel/3d/texture_bump.h"
26 #ifdef DEBUG_NEW
27 #define new DEBUG_NEW
28 #endif
30 namespace NL3D {
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
37 uint NumRefs;
38 float NormalizationFactor;
40 typedef std::map<std::string, CNormalizationInfo> TNameToNI; // sharename to the normalization factor
41 class CNameToNFStatic {
42 public:
43 TNameToNI Map;
44 bool Initialized;
45 CNameToNFStatic() : Initialized(true)
49 ~CNameToNFStatic()
51 Initialized = false;
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))
63 sint x, y;
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))
81 sint x, y;
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;
99 uint k;
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)
109 return 1.f;
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)
127 uint k;
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)
140 return 1.f;
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;
162 * Constructor
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;
184 touch();
189 ///==============================================================================================
190 void CTextureBump::serial(NLMISC::IStream &f)
192 /// version 2 : normalization flag
193 sint ver = f.serialVersion(3);
194 ITexture::serial(f);
195 ITexture *tex = NULL;
196 if (f.isReading())
198 f.serialPolyPtr(tex);
199 _HeightMap = tex;
200 touch();
202 else
204 tex = _HeightMap;
205 f.serialPolyPtr(tex);
207 f.serial(_DisableSharing);
208 if (ver >= 1)
210 bool oldFlag = false;
211 f.serial(oldFlag); // backaward compatibility : serial the old "forceAbsoluteOffset" flag
213 if (ver >= 2)
215 f.serial(_ForceNormalize);
219 ///==============================================================================================
220 void CTextureBump::doGenerate(bool async)
222 if (!_HeightMap)
224 makeDummy();
225 return;
227 // generate the height map
228 _HeightMap->generate();
229 if (!_HeightMap->convertToType(CBitmap::RGBA))
231 makeDummy();
232 return;
234 releaseMipMaps();
235 uint width = _HeightMap->getWidth();
236 uint height = _HeightMap->getHeight();
237 if (getUploadFormat() == RGBA8888)
239 CBitmap::resize(_HeightMap->getWidth(), _HeightMap->getHeight(), CBitmap::RGBA);
241 else
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]));
250 else
252 BuildDsDt((uint32 *) &(_HeightMap->getPixels()[0]), width, height, (uint16 *) &(getPixels()[0]));
255 float normalizationFactor = 1.0f;
256 // Normalize the map if needed
257 if (_ForceNormalize)
259 if (getUploadFormat() == RGBA8888)
261 normalizationFactor = NormalizeDsDtAsRGBA((uint32 *) &(getPixels()[0]), width, height);
263 else
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;
276 ni.NumRefs = 1;
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);
281 else
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()
297 ITexture::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()
325 if (!_Touched)
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);
339 else
341 doGenerate();
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());
355 // if found
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);
367 } // NL3D