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) 2016 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_dlm.h"
23 #include "nel/misc/common.h"
24 #include "nel/misc/fast_mem.h"
28 using namespace NLMISC
;
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)
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
);
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!!
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);
101 nlassert(width
==2 || width
==3 || width
==5 || width
==9);
102 nlassert(height
==2 || height
==3 || height
==5 || height
==9);
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
113 uint id
= width
+ height
*4;
114 nlassert(id
<NL_DLM_LIGHTMAP_TYPE_SIZE
);
120 // ***************************************************************************
121 bool CTextureDLM::canCreateLightMap(uint w
, uint h
)
123 // First test if the list is not empty.
124 if(_FreeBlocks
[getTypeForSize(w
,h
)])
127 // If empty, test if there is an empty block.
128 return !_EmptyBlocks
.empty();
132 // ***************************************************************************
133 void CTextureDLM::linkFreeBlock(uint lMapType
, CBlock
*block
)
136 block
->FreeNext
= _FreeBlocks
[lMapType
];
137 block
->FreePrec
= NULL
;
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
149 block
->FreeNext
->FreePrec
= block
->FreePrec
;
151 block
->FreePrec
->FreeNext
= block
->FreeNext
;
153 _FreeBlocks
[lMapType
]= block
->FreeNext
;
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
))
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();
182 nlassert(block
->FreeSpace
==0);
183 // set size of lightmaps for this blocks.
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.
206 for(i
= 0;i
<nLMapPerBlock
; 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
;
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
;
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
);
237 // ***************************************************************************
238 void CTextureDLM::copyRect(uint x
, uint y
, uint w
, uint h
, CRGBA
*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
)
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
));
281 for(sint n
= h
;n
>0;n
--, dst
+= (getWidth()-w
) )
284 for(sint nc
= w
;nc
>0;nc
--, textMap
++, modColor
++, dst
++)
286 uint16 tc
= *modColor
;
288 dst
->R
= ( (tc
>>11) * textMap
->R
)>>5;
290 dst
->G
= (((tc
>>5)&63) * textMap
->G
)>>6;
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
));
313 for(sint n
= h
;n
>0;n
--, dst
+= (getWidth()-w
) )
316 for(sint nc
= w
;nc
>0;nc
--, textMap
++, modColor
++, dst
++)
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
));
342 for(sint n
= h
;n
>0;n
--, dst
+= (getWidth()-w
) )
345 for(sint nc
= w
;nc
>0;nc
--, textMap
++, dst
++)
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.
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
;
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.