Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / texture_dlm.cpp
blob71ec5c2bb9a122fb55df50e3e2d44deb16ac042b
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) 2016 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_dlm.h"
23 #include "nel/misc/common.h"
24 #include "nel/misc/fast_mem.h"
27 using namespace std;
28 using namespace NLMISC;
30 #ifdef DEBUG_NEW
31 #define new DEBUG_NEW
32 #endif
34 namespace NL3D
38 // ***************************************************************************
39 CTextureDLM::CTextureDLM(uint width, uint height)
41 nlassert(width>=NL_DLM_BLOCK_SIZE);
42 nlassert(height>=NL_DLM_BLOCK_SIZE);
43 nlassert(NLMISC::isPowerOf2(width));
44 nlassert(NLMISC::isPowerOf2(height));
46 // verify there is sufficient blocks.
47 _WBlock= width/NL_DLM_BLOCK_SIZE;
48 uint nBlocks= _WBlock * (height/NL_DLM_BLOCK_SIZE);
49 nlassert(nBlocks>=NL_DLM_LIGHTMAP_TYPE_SIZE);
52 // The DLM texture always reside in memory...
53 // NB: this is simplier like that, and this is not a problem, since actually only 256Ko is allocated :o)
54 setReleasable(false);
55 // create the bitmap.
56 CBitmap::resize(width, height, CBitmap::RGBA);
57 // Format of texture, 32 bits and no mipmaps.
58 // NB: 16 bits is not a good idea, because implies lot of flicking
59 setUploadFormat(ITexture::RGBA8888);
60 setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff);
62 // Fill the array of blocks.
63 _Blocks.resize(nBlocks);
64 _EmptyBlocks.resize(nBlocks);
65 uint i;
66 for(i = 0; i < _Blocks.size(); i++)
68 // compute position of the block in the texture
69 _Blocks[i].PosX = (i%_WBlock) * NL_DLM_BLOCK_SIZE;
70 _Blocks[i].PosY = (i/_WBlock) * NL_DLM_BLOCK_SIZE;
72 // This block is free!!
73 _EmptyBlocks[i] = i;
76 // init list to NULL.
77 for(i = 0; i < NL_DLM_LIGHTMAP_TYPE_SIZE; i++)
79 _FreeBlocks[i] = NULL;
82 // Since NL_DLM_BLOCK_SIZE is 10 or 18 (a factor of prime number 5 or 3 respectively), we are sure there is
83 // at least one pixel which is not used by blcks. The last pixel is filled with black (see CTextureDLm doc)
84 nlassert(NL_DLM_BLOCK_SIZE==10 || NL_DLM_BLOCK_SIZE==18);
85 CRGBA *ptr = (CRGBA*)(&CBitmap::getPixels(0)[0]);
86 // fill last pixel with black.
87 ptr[width*height-1]= CRGBA::Black;
88 // Also, to ensure the texture do not wrap around, disable Tiling.
89 ITexture::setWrapS(ITexture::Clamp);
90 ITexture::setWrapT(ITexture::Clamp);
94 // ***************************************************************************
95 uint CTextureDLM::getTypeForSize(uint width, uint height)
97 #ifdef NL_DLM_TILE_RES
98 nlassert(width==3 || width==5 || width==9 || width==17);
99 nlassert(height==3 || height==5 || height==9 || height==17);
100 #else
101 nlassert(width==2 || width==3 || width==5 || width==9);
102 nlassert(height==2 || height==3 || height==5 || height==9);
103 #endif
105 // 0 for 2, 1 for 3, 2 for 5, and 3 for 9, and 4 for 17
106 width= getPowerOf2(width-1);
107 height= getPowerOf2(height-1);
108 #ifdef NL_DLM_TILE_RES
109 // 0 for 3, 1 for 5, 2, for 9, and 3 for 17
110 width--; height--;
111 #endif
113 uint id= width + height*4;
114 nlassert(id<NL_DLM_LIGHTMAP_TYPE_SIZE);
116 return id;
120 // ***************************************************************************
121 bool CTextureDLM::canCreateLightMap(uint w, uint h)
123 // First test if the list is not empty.
124 if(_FreeBlocks[getTypeForSize(w,h)])
125 return true;
127 // If empty, test if there is an empty block.
128 return !_EmptyBlocks.empty();
132 // ***************************************************************************
133 void CTextureDLM::linkFreeBlock(uint lMapType, CBlock *block)
135 // link me to others
136 block->FreeNext= _FreeBlocks[lMapType];
137 block->FreePrec= NULL;
138 // link other to me
139 if(_FreeBlocks[lMapType])
140 _FreeBlocks[lMapType]->FreePrec= block;
141 _FreeBlocks[lMapType]= block;
144 // ***************************************************************************
145 void CTextureDLM::unlinkFreeBlock(uint lMapType, CBlock *block)
147 // unlink other from me
148 if(block->FreeNext)
149 block->FreeNext->FreePrec= block->FreePrec;
150 if(block->FreePrec)
151 block->FreePrec->FreeNext= block->FreeNext;
152 else
153 _FreeBlocks[lMapType]= block->FreeNext;
154 // reset me
155 block->FreePrec= NULL;
156 block->FreeNext= NULL;
160 // ***************************************************************************
161 bool CTextureDLM::createLightMap(uint w, uint h, uint &x, uint &y)
163 // at least cna create it??
164 if(!canCreateLightMap(w, h))
165 return false;
167 // the type of lightmap.
168 uint lMapType= getTypeForSize(w,h);
170 // First manage case list is empty.
171 //===================
172 if(_FreeBlocks[lMapType]==NULL)
174 // list is empty => allocate a block from _EmptyBlocks.
175 nlassert(!_EmptyBlocks.empty());
177 // pop a block from empty list
178 CBlock *block= &_Blocks[_EmptyBlocks.back()];
179 _EmptyBlocks.pop_back();
181 // init this block.
182 nlassert(block->FreeSpace==0);
183 // set size of lightmaps for this blocks.
184 block->Width= w;
185 block->Height= h;
187 // Link this block to the list.
188 linkFreeBlock(lMapType, block);
191 // Get the block from the list.
192 CBlock *block= _FreeBlocks[lMapType];
194 // Allocate block lightmap.
195 //===================
197 // compute block info.
198 uint nLMapOnX= NL_DLM_BLOCK_SIZE / block->Width;
199 uint nLMapOnY= NL_DLM_BLOCK_SIZE / block->Height;
200 uint nLMapPerBlock= nLMapOnX * nLMapOnY;
201 // bit must fit in a uint64
202 nlassert(nLMapPerBlock<=64);
204 // get an id in the FreeSpace bitField.
205 uint i;
206 for(i= 0;i<nLMapPerBlock; i++)
208 uint mask= 1<<i;
209 // If the bit is not set, then this id is free.
210 if( (block->FreeSpace & mask)==0 )
212 // take this id, hence set this bit
213 block->FreeSpace|= mask;
214 // stop, found.
215 break;
218 nlassert(i<nLMapPerBlock);
220 // compute x/y texture pos for this id.
221 x= block->PosX + (i%nLMapOnX) * w;
222 y= block->PosY + (i/nLMapOnX) * h;
225 // if lightmap full
226 //===================
227 // if bitfield is full
228 if( block->FreeSpace == (uint)(1<<nLMapPerBlock)-1 )
230 // Must remove it from free list.
231 unlinkFreeBlock(lMapType, block);
234 return true;
237 // ***************************************************************************
238 void CTextureDLM::copyRect(uint x, uint y, uint w, uint h, CRGBA *textMap)
240 // copy image.
241 CRGBA *src= textMap;
242 CRGBA *dst= (CRGBA*)getPixels().getPtr();
243 dst+= y*getWidth()+x;
244 for(sint n= h;n>0;n--, src+= w, dst+= getWidth())
246 memcpy(dst, src, w*sizeof(CRGBA));
249 // Invalidate the rectangle.
250 ITexture::touchRect(CRect(x, y, w, h));
253 // ***************************************************************************
254 void CTextureDLM::fillRect(uint x, uint y, uint w, uint h, uint8 value)
256 // copy image.
257 CRGBA *dst= (CRGBA*)getPixels().getPtr();
258 dst+= y*getWidth()+x;
259 for(sint n= h;n>0;n--, dst+= getWidth())
261 memset(dst, value, w*sizeof(CRGBA));
264 // Invalidate the rectangle.
265 ITexture::touchRect(CRect(x, y, w, h));
269 // ***************************************************************************
270 void CTextureDLM::modulateAndfillRect565(uint x, uint y, uint w, uint h, CRGBA *textMap, uint16 *modColor)
272 // compute start dst to copy.
273 CRGBA *dst= (CRGBA*)getPixels().getPtr();
274 dst+= y*getWidth()+x;
276 // precahce Data in memory (best CPU support)
277 CFastMem::precache(textMap, w*h*sizeof(CRGBA));
278 CFastMem::precache(modColor, w*h*sizeof(uint16));
280 // For all lines
281 for(sint n= h;n>0;n--, dst+= (getWidth()-w) )
283 // For all the line.
284 for(sint nc= w;nc>0;nc--, textMap++, modColor++, dst++)
286 uint16 tc= *modColor;
287 // modulate R.
288 dst->R= ( (tc>>11) * textMap->R)>>5;
289 // modulate G.
290 dst->G= (((tc>>5)&63) * textMap->G)>>6;
291 // modulate B.
292 dst->B= ( (tc&31) * textMap->B)>>5;
296 // Invalidate the rectangle.
297 ITexture::touchRect(CRect(x, y, w, h));
301 // ***************************************************************************
302 void CTextureDLM::modulateAndfillRect8888(uint x, uint y, uint w, uint h, CRGBA *textMap, CRGBA *modColor)
304 // compute start dst to copy.
305 CRGBA *dst= (CRGBA*)getPixels().getPtr();
306 dst+= y*getWidth()+x;
308 // precahce Data in memory (best CPU support)
309 CFastMem::precache(textMap, w*h*sizeof(CRGBA));
310 CFastMem::precache(modColor, w*h*sizeof(CRGBA));
312 // For all lines
313 for(sint n= h;n>0;n--, dst+= (getWidth()-w) )
315 // For all the line.
316 for(sint nc= w;nc>0;nc--, textMap++, modColor++, dst++)
318 CRGBA mc= *modColor;
319 // modulate RGB only
320 dst->R= ( mc.R * textMap->R)>>8;
321 dst->G= ( mc.G * textMap->G)>>8;
322 dst->B= ( mc.B * textMap->B)>>8;
326 // Invalidate the rectangle.
327 ITexture::touchRect(CRect(x, y, w, h));
331 // ***************************************************************************
332 void CTextureDLM::modulateConstantAndfillRect(uint x, uint y, uint w, uint h, CRGBA *textMap, CRGBA mc)
334 // compute start dst to copy.
335 CRGBA *dst= (CRGBA*)getPixels().getPtr();
336 dst+= y*getWidth()+x;
338 // precahce Data in memory (best CPU support)
339 CFastMem::precache(textMap, w*h*sizeof(CRGBA));
341 // For all lines
342 for(sint n= h;n>0;n--, dst+= (getWidth()-w) )
344 // For all the line.
345 for(sint nc= w;nc>0;nc--, textMap++, dst++)
347 // modulate RGB only
348 dst->R= ( mc.R * textMap->R)>>8;
349 dst->G= ( mc.G * textMap->G)>>8;
350 dst->B= ( mc.B * textMap->B)>>8;
354 // Invalidate the rectangle.
355 ITexture::touchRect(CRect(x, y, w, h));
359 // ***************************************************************************
360 void CTextureDLM::releaseLightMap(uint x, uint y)
362 // Search what block is under this pos.
363 uint blockId= (y/NL_DLM_BLOCK_SIZE)*_WBlock + (x/NL_DLM_BLOCK_SIZE);
364 nlassert(blockId<_Blocks.size());
365 CBlock *block= &_Blocks[blockId];
367 // compute block info.
368 uint nLMapOnX= NL_DLM_BLOCK_SIZE / block->Width;
369 uint nLMapOnY= NL_DLM_BLOCK_SIZE / block->Height;
370 uint nLMapPerBlock= nLMapOnX * nLMapOnY;
371 // was Full (ie all bits set) before this release
372 bool wasFull= (block->FreeSpace == (uint)(1<<nLMapPerBlock)-1);
373 // the type of lightmap.
374 uint lMapType= getTypeForSize(block->Width, block->Height);
377 // get relative pos to the block.
378 x-= block->PosX;
379 y-= block->PosY;
381 // compute bit number.
382 uint bitX= x/block->Width;
383 uint bitY= y/block->Height;
384 // assert good pos param.
385 nlassert(x == bitX*block->Width);
386 nlassert(y == bitY*block->Height);
388 // compute bitId, as done in createLightMap()
389 uint bitId= bitY * nLMapOnX + bitX;
390 uint mask= 1<<bitId;
392 // Free this bit in the block.
393 nlassert(block->FreeSpace & mask);
394 block->FreeSpace&= (~mask & std::numeric_limits<uint>::max());
397 // Free the block if necessary.
398 //=======================
399 bool isEmpty= block->FreeSpace==0;
401 // If wasFull and now it is empty (nLMapPerBlock==1 case), just append to EmptyBlocks.
402 if(wasFull && isEmpty)
404 // add the id to the empty list.
405 _EmptyBlocks.push_back(blockId);
407 // if wasFull, but still have some lightmap now, must insert into FreeList
408 else if(wasFull && !isEmpty)
410 linkFreeBlock(lMapType, block);
412 // if was not full but now it is empty, must remove from free list and insert into EmptyBlocks
413 else if(!wasFull && isEmpty)
415 // remove the block from Free List
416 unlinkFreeBlock(lMapType, block);
417 // add the id to the empty list.
418 _EmptyBlocks.push_back(blockId);
420 // else (!wasFull && !isEmpty) no-op, since must be kept in the FreeList.
425 } // NL3D