Change Encyclo button name and macros icon
[ryzomcore.git] / nel / src / 3d / patchdlm_context.cpp
blob54a34dd92642a941ad235bd2a4b8d6aa0e613a2c
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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.
8 //
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/>.
17 #include "std3d.h"
19 #include "nel/3d/patchdlm_context.h"
20 #include "nel/3d/patch.h"
21 #include "nel/3d/bezier_patch.h"
22 #include "nel/3d/point_light.h"
23 #include "nel/3d/texture_dlm.h"
24 #include "nel/misc/fast_floor.h"
25 #include "nel/3d/tile_far_bank.h"
26 #include "nel/3d/landscape.h"
27 #include "nel/misc/system_info.h"
28 #include "nel/misc/fast_mem.h"
31 using namespace std;
32 using namespace NLMISC;
34 #ifdef DEBUG_NEW
35 #define new DEBUG_NEW
36 #endif
38 namespace NL3D
41 // ***************************************************************************
42 // ***************************************************************************
43 // ***************************************************************************
46 // ***************************************************************************
47 void CPatchDLMPointLight::compile(const CPointLight &pl, NLMISC::CRGBA landDiffMat, float maxAttEnd)
49 nlassert(maxAttEnd>0);
51 // copy color
52 R= (float) (( pl.getDiffuse().R*(landDiffMat.R+1) ) >>8);
53 G= (float) (( pl.getDiffuse().G*(landDiffMat.G+1) ) >>8);
54 B= (float) (( pl.getDiffuse().B*(landDiffMat.B+1) ) >>8);
55 // Copy Spot/Pos/Dir.
56 IsSpot= pl.getType() == CPointLight::SpotLight;
57 Pos= pl.getPosition();
58 Dir= pl.getSpotDirection();
60 // compute spot params
61 if(IsSpot)
63 CosMax= cosf(pl.getSpotAngleBegin());
64 CosMin= cosf(pl.getSpotAngleEnd());
66 else
68 // with tesse Values, we have always (cosSpot-CosMin) * OOCosDelta > 1.0f
69 CosMax= -1;
70 CosMin= -2;
72 OOCosDelta= 1.f / (CosMax-CosMin);
74 // compute att params
75 AttMax= pl.getAttenuationEnd();
76 AttMin= pl.getAttenuationBegin();
77 // infinite pointLight?
78 if(AttMax==0)
80 AttMax= maxAttEnd;
81 AttMin= maxAttEnd*0.99f;
83 // To big pointLigt?
84 else if(AttMax>maxAttEnd)
86 AttMax= maxAttEnd;
87 AttMin= min(AttMin, maxAttEnd*0.99f);
89 // compile distance
90 OOAttDelta= 1.f / (AttMin-AttMax);
93 // Compute bounding sphere.
94 // If not a spot or if angleMin>Pi/2
95 if(!IsSpot || CosMin<0)
97 // Take sphere of pointlight sphere
98 BSphere.Center= Pos;
99 BSphere.Radius= AttMax;
100 // The bbox englobe the sphere.
101 BBox.setCenter(Pos);
102 BBox.setHalfSize(CVector(AttMax, AttMax, AttMax));
104 else
106 // Compute BSphere.
107 //==============
109 // compute sinus of AngleMin
110 float sinMin= sqrtf(1-sqr(CosMin));
112 // Test 2 centers: Center of radius along Dir: Pos+Dir*AttMax/2, and intersection of end cone with line (Pos,Dir)
113 // Don't know why but I think they are sufficiently good :)
114 // See below for computing of those centers.
116 /* compute radius of each sphere by taking max of 3 distances: distance to spotLight center, distance
117 to spotLight forward extremity, and distance to spotLight circle interstion Cone/Sphere. (named DCCS)
118 NB: Do the compute with radius=1 at first, then multiply later.
120 float radius1= 0.5f; // =max(0.5, 0.5); max distance to spot center and extremity center :)
121 // for distance DCCS, this is the hypothenuse of (cosMin-0.5) + sinMin.
122 float dccs= sqrtf( sqr(CosMin-0.5f) + sqr(sinMin));
123 // take the bigger.
124 radius1= max(radius1, dccs );
126 // Same reasoning for center2.
127 float radius2= max(CosMin, 1-CosMin); // max distance to spot center and extremity center :)
128 // for distance DCCS, it is simply sinMin!!
129 dccs= sinMin;
130 // take the bigger.
131 radius2= max(radius2, dccs );
134 // Then take the center which gives the smaller sphere
135 if(radius1<radius2)
137 BSphere.Center= Pos + (Dir*0.5f*AttMax);
138 // radius1 E [0,1], must take real size.
139 BSphere.Radius= radius1 * AttMax;
141 else
143 BSphere.Center= Pos + (Dir*CosMin*AttMax);
144 // radius2 E [0,1], must take real size.
145 BSphere.Radius= radius2 * AttMax;
149 // Compute BBox.
150 //==============
152 // just take bbox of the sphere, even if not optimal.
153 BBox.setCenter(BSphere.Center);
154 float rad= BSphere.Radius;
155 BBox.setHalfSize( CVector(rad, rad, rad) );
160 // ***************************************************************************
161 // ***************************************************************************
162 // ***************************************************************************
165 // ***************************************************************************
166 CPatchDLMContext::CPatchDLMContext()
168 _Patch= NULL;
169 _DLMTexture= NULL;
170 _DLMContextList= NULL;
171 OldPointLightCount= 0;
172 CurPointLightCount= 0;
173 // By default there is crash in textures
174 _IsSrcTextureFullBlack= false;
175 _IsDstTextureFullBlack= false;
179 // ***************************************************************************
180 CPatchDLMContext::~CPatchDLMContext()
182 // release the lightmap in the texture
183 if(_DLMTexture)
185 _DLMTexture->releaseLightMap(TextPosX, TextPosY);
187 // exit
188 _Patch= NULL;
189 _DLMTexture= NULL;
191 // remove it from list.
192 if(_DLMContextList)
193 _DLMContextList->remove(this);
197 // ***************************************************************************
198 #ifdef NL_DLM_TILE_RES
199 // if tileRes defined, still start to clip at tessBlockLevel.
200 #define NL_DLM_CLIP_FACTOR 2
201 #else
202 // start to clip at tessBlockLevel (same as dlm map precision)
203 #define NL_DLM_CLIP_FACTOR 1
204 #endif
206 #define NL_DLM_CLIP_NUM_LEVEL 3
208 // ***************************************************************************
209 bool CPatchDLMContext::generate(CPatch *patch, CTextureDLM *textureDLM, CPatchDLMContextList *ctxList)
211 nlassert(patch);
212 nlassert(textureDLM);
213 nlassert(ctxList);
215 // keep info on patch/landscape.
216 _Patch= patch;
217 _DLMTexture= textureDLM;
218 // append to the list.
219 _DLMContextList= ctxList;
220 _DLMContextList->append(this);
222 // Get Texture Size info;
223 #ifdef NL_DLM_TILE_RES
224 // get coord at cornes of tiles
225 Width= (_Patch->getOrderS())+1;
226 Height= (_Patch->getOrderT())+1;
227 #else
228 // get coord at cornes of tessBlocks
229 Width= (_Patch->getOrderS()/2)+1;
230 Height= (_Patch->getOrderT()/2)+1;
231 #endif
233 // Allocate space in texture
234 if(!_DLMTexture->createLightMap(Width, Height, TextPosX, TextPosY))
236 // Mark as not allocated.
237 // NB: the context still work with NULL _DLMTexture, but do nothing (excpetionnal case)
238 _DLMTexture= NULL;
241 // If the lightmap is correclty allocated in the global texture, compute UVBias.
242 if(_DLMTexture)
244 // Compute patch UV matrix from pixels. Must map to center of pixels.
245 DLMUScale= (float)(Width-1) / (float)_DLMTexture->getWidth();
246 DLMVScale= (float)(Height-1) / (float)_DLMTexture->getHeight();
247 DLMUBias= ((float)TextPosX+0.5f) / (float)_DLMTexture->getWidth();
248 DLMVBias= ((float)TextPosY+0.5f) / (float)_DLMTexture->getHeight();
250 else
252 // Build UVBias such that the UVs point to Black
253 // NB: TextureDLM ensure that point (MaxX,MaxY) of texture is black.
254 DLMUScale= 0;
255 DLMVScale= 0;
256 DLMUBias= 1;
257 DLMVBias= 1;
260 // TestYoyo: to see lightmap usage in the big texture
261 /*DLMUScale= _Patch->getOrderS();
262 DLMVScale= _Patch->getOrderT();
263 DLMUBias= 0;
264 DLMVBias= 0;*/
267 // Bound 8bits UV for Vegetable. This is to ensure vegetable Dlm UVs won't peek in neighbor lightmaps.
268 sint tmpU, tmpV;
269 // Bound U minimum
270 tmpU= (sint)ceil ( (DLMUBias) * 255 );
271 clamp(tmpU, 0, 255);
272 MinU8= tmpU;
273 // Bound U maximum
274 tmpU= (sint)floor( (DLMUBias+DLMUScale) * 255 );
275 clamp(tmpU, (sint)MinU8, 255);
276 MaxU8= tmpU;
277 // Bound V minimum
278 tmpV= (sint)ceil ( (DLMVBias) * 255 );
279 clamp(tmpV, 0, 255);
280 MinV8= tmpV;
281 // Bound V maximum
282 tmpV= (sint)floor( (DLMVBias+DLMVScale) * 255 );
283 clamp(tmpV, (sint)MinV8, 255);
284 MaxV8= tmpV;
287 // Allocate RAM Lightmap
288 _LightMap.resize(Width*Height);
290 // generate Vertices: pos and normals
291 _Vertices.resize(Width*Height);
292 float s, t;
293 float ds= 1.0f / (Width-1);
294 float dt= 1.0f / (Height-1);
295 // eval all the patch.
296 t= 0;
297 uint x,y;
298 for(y=0; y<Height; y++, t+=dt)
300 s= 0;
301 for(x=0; x<Width; x++, s+=ds)
303 CVertex &vert= _Vertices[y*Width+x];
304 // NB: use the bezier patch, and don't take Noise into account, for speed reason.
305 CBezierPatch *bpatch= _Patch->unpackIntoCache();
306 // Eval pos.
307 vert.Pos= bpatch->eval(s, t);
308 // Eval Normal.
309 vert.Normal= bpatch->evalNormal(s, t);
313 // Build bounding Spheres QuadTree
314 //============
316 // Size of the cluster array (at level 0)
317 uint bsx, bsy;
318 #ifdef NL_DLM_TILE_RES
319 // level 0 is at tile level.
320 bsx= max(1, (_Patch->getOrderS())/NL_DLM_CLIP_FACTOR );
321 bsy= max(1, (_Patch->getOrderT())/NL_DLM_CLIP_FACTOR );
322 #else
323 // level 0 is at tessBlock level.
324 bsx= max(1, (_Patch->getOrderS()/2)/NL_DLM_CLIP_FACTOR );
325 bsy= max(1, (_Patch->getOrderT()/2)/NL_DLM_CLIP_FACTOR );
326 #endif
328 // resize bboxes for level 0.
329 static vector<CAABBox> tmpBBoxes[NL_DLM_CLIP_NUM_LEVEL];
330 tmpBBoxes[0].resize(bsx * bsy);
332 // Extend all leaves clusters BBoxes with patch coordinates
333 for(y=0;y<bsy;y++)
335 // For Y, compute how many patch Positions used to extend bbox.
336 uint beginY= y*NL_DLM_CLIP_FACTOR;
337 uint endY= min( (y+1)*NL_DLM_CLIP_FACTOR+1, Height);
338 for(x=0;x<bsx;x++)
340 // For X, compute how many patch Positions used to extend bbox.
341 uint beginX= x*NL_DLM_CLIP_FACTOR;
342 uint endX= min((x+1)*NL_DLM_CLIP_FACTOR+1, Width);
343 // Build a bbox.
344 CAABBox bbox;
345 bbox.setCenter(_Vertices[beginY*Width + beginX].Pos);
346 for(uint yi= beginY; yi<endY; yi++)
348 for(uint xi= beginX; xi<endX; xi++)
350 bbox.extend(_Vertices[yi*Width + xi].Pos);
353 // Set the BBox info.
354 tmpBBoxes[0][y*bsx + x]= bbox;
358 // build parent BSpheres for quadTree hierarchy
359 uint curLevel= 0;
360 uint nextLevel= 1;
361 uint nextBsx= max(1U, bsx/2);
362 uint nextBsy= max(1U, bsy/2);
363 // the number of cluster Sons, and descendants this cluster level owns.
364 uint tmpClusterNumToSkip[NL_DLM_CLIP_NUM_LEVEL];
365 // width for this cluster level.
366 uint tmpClusterWidth[NL_DLM_CLIP_NUM_LEVEL];
367 // Number of sons per line/column
368 uint tmpClusterWSon[NL_DLM_CLIP_NUM_LEVEL];
369 uint tmpClusterHSon[NL_DLM_CLIP_NUM_LEVEL];
370 // Fill level 0 info
371 tmpClusterNumToSkip[0]= 0;
372 tmpClusterWidth[0]= bsx;
373 tmpClusterWSon[0]= 0;
374 tmpClusterHSon[0]= 0;
375 uint finalClusterSize= bsx * bsy;
377 // If the next level has 1x1 cases, it is not useful (since same sphere as entire Patch)
378 while(nextBsx * nextBsy > 1 && nextLevel<NL_DLM_CLIP_NUM_LEVEL )
380 finalClusterSize+= nextBsx * nextBsy;
382 uint wSon= (bsx/nextBsx);
383 uint hSon= (bsy/nextBsy);
384 // compute cluster level info.
385 tmpClusterWidth[nextLevel]= nextBsx;
386 tmpClusterWSon[nextLevel]= wSon;
387 tmpClusterHSon[nextLevel]= hSon;
388 // NB: level 0 has 0 sons to skip, hence level1 must skip (1+0)*4= 4 (wSon==hSon==2)
389 // level2 must skip (1+4)*4= 20 (wSon==hSon==2)
390 tmpClusterNumToSkip[nextLevel]= (1+tmpClusterNumToSkip[curLevel]) * wSon * hSon;
392 // alloc bboxes.
393 tmpBBoxes[nextLevel].resize(nextBsx * nextBsy);
395 // For all cluster of upper level, build bb, as union of finers clusters
396 for(y=0;y<nextBsy;y++)
398 for(x=0;x<nextBsx;x++)
400 // compute coordinate in curLevel tmpBBoxes to look
401 uint x2= x*wSon;
402 uint y2= y*hSon;
403 // Build a bbox for 4 (or 2) children clusters
404 if(wSon>1 && hSon>1)
406 CAABBox bbox1;
407 CAABBox bbox2;
408 bbox1= CAABBox::computeAABBoxUnion(
409 tmpBBoxes[curLevel][y2*bsx + x2], tmpBBoxes[curLevel][y2*bsx + x2+1]);
410 bbox2= CAABBox::computeAABBoxUnion(
411 tmpBBoxes[curLevel][(y2+1)*bsx + x2], tmpBBoxes[curLevel][(y2+1)*bsx + x2+1]);
412 // final father bbox.
413 tmpBBoxes[nextLevel][y*nextBsx + x]= CAABBox::computeAABBoxUnion(bbox1, bbox2);
415 else if(wSon==1)
417 CAABBox bbox1;
418 bbox1= CAABBox::computeAABBoxUnion(
419 tmpBBoxes[curLevel][y2*bsx + x2], tmpBBoxes[curLevel][(y2+1)*bsx + x2]);
420 // final father bbox.
421 tmpBBoxes[nextLevel][y*nextBsx + x]= bbox1;
423 else if(hSon==1)
425 CAABBox bbox1;
426 bbox1= CAABBox::computeAABBoxUnion(
427 tmpBBoxes[curLevel][y2*bsx + x2], tmpBBoxes[curLevel][y2*bsx + x2+1]);
428 // final father bbox.
429 tmpBBoxes[nextLevel][y*nextBsx + x]= bbox1;
431 else
432 // impossible...
433 nlstop;
437 // upper level.
438 bsx= nextBsx;
439 bsy= nextBsy;
440 nextBsx= max(1U, nextBsx/2);
441 nextBsy= max(1U, nextBsy/2);
442 curLevel++;
443 nextLevel++;
447 // Resize clusters with size according to all levels
448 _Clusters.resize(finalClusterSize);
449 uint iDstCluster= 0;
451 // Fill cluster hierarchy, in _Clusters.
452 uint numLevels= nextLevel;
453 // NB: the principle is recursive, but it is "iterated", with a stack-like: tmpClusterX and tmpClusterY;
454 uint tmpClusterX[NL_DLM_CLIP_NUM_LEVEL];
455 uint tmpClusterY[NL_DLM_CLIP_NUM_LEVEL];
456 uint tmpClusterXMin[NL_DLM_CLIP_NUM_LEVEL];
457 uint tmpClusterYMin[NL_DLM_CLIP_NUM_LEVEL];
458 uint tmpClusterXMax[NL_DLM_CLIP_NUM_LEVEL];
459 uint tmpClusterYMax[NL_DLM_CLIP_NUM_LEVEL];
460 // we start at curLevel (the highest Level), and we must fill all the squares of this level
461 tmpClusterX[curLevel]= 0;
462 tmpClusterY[curLevel]= 0;
463 tmpClusterXMin[curLevel]= 0;
464 tmpClusterYMin[curLevel]= 0;
465 tmpClusterXMax[curLevel]= bsx;
466 tmpClusterYMax[curLevel]= bsy;
467 // while the "root" level is not pop
468 while(curLevel < numLevels)
470 // If we ended with this level (all lines done).
471 if(tmpClusterY[curLevel] >= tmpClusterYMax[curLevel])
473 // Ok, finished with this level, pop up.
474 curLevel++;
475 // skip.
476 continue;
479 nlassert(iDstCluster<_Clusters.size());
481 // get the bbox from current position.
482 CAABBox bbox= tmpBBoxes[curLevel][ tmpClusterY[curLevel] * tmpClusterWidth[curLevel] + tmpClusterX[curLevel] ];
483 // Fill _Clusters for this square.
484 _Clusters[iDstCluster].BSphere.Center= bbox.getCenter();
485 _Clusters[iDstCluster].BSphere.Radius= bbox.getRadius();
486 // If leaf level, fill special info
487 if(curLevel == 0)
489 _Clusters[iDstCluster].NSkips= 0;
490 _Clusters[iDstCluster].X= tmpClusterX[0];
491 _Clusters[iDstCluster].Y= tmpClusterY[0];
493 // else, set total number of sons to skips if "invisible"
494 else
495 _Clusters[iDstCluster].NSkips= tmpClusterNumToSkip[curLevel];
497 // next dst cluster
498 iDstCluster ++;
501 // If not Leaf level, recurs. First pass, use curLevel params (tmpClusterX...)
502 if(curLevel > 0)
504 // compute info for next level.
505 tmpClusterXMin[curLevel-1]= tmpClusterX[curLevel] * tmpClusterWSon[curLevel];
506 tmpClusterYMin[curLevel-1]= tmpClusterY[curLevel] * tmpClusterHSon[curLevel];
507 tmpClusterXMax[curLevel-1]= (tmpClusterX[curLevel]+1) * tmpClusterWSon[curLevel];
508 tmpClusterYMax[curLevel-1]= (tmpClusterY[curLevel]+1) * tmpClusterHSon[curLevel];
509 // begin iteration of child level
510 tmpClusterX[curLevel-1]= tmpClusterXMin[curLevel-1];
511 tmpClusterY[curLevel-1]= tmpClusterYMin[curLevel-1];
515 // next square for this level
516 tmpClusterX[curLevel]++;
517 // if ended for X.
518 if(tmpClusterX[curLevel] >= tmpClusterXMax[curLevel])
520 // reset X.
521 tmpClusterX[curLevel]= tmpClusterXMin[curLevel];
522 // next line.
523 tmpClusterY[curLevel]++;
527 // If not Leaf level, recurs. Second pass, after tmpClusterX and tmpClusterY of curLevel are changed
528 if(curLevel > 0)
530 // descend in hierarchy. (recurs)
531 curLevel--;
536 // All dst clusters must have been filled
537 nlassert(iDstCluster == _Clusters.size());
540 // PreProcess Patch TileColors.
541 //============
542 // Verify that a CTileColor is nothing more than a 565 color.
543 nlassert(sizeof(CTileColor)==sizeof(uint16));
544 #ifndef NL_DLM_TILE_RES
546 // retrieve patch tileColor pointer.
547 nlassert(!_Patch->TileColors.empty());
548 CTileColor *tileColor= &_Patch->TileColors[0];
550 // skip 1 tiles colors per column and per row
551 uint wTileColor= _Patch->getOrderS()+1;
552 CTileColor *tcOrigin= tileColor;
553 // alloc _LowResTileColors at same resolution than lightmap
554 _LowResTileColors.resize(Width*Height);
555 uint16 *dstLRtc= &_LowResTileColors[0];
557 // For all lines of dst.
558 for(y=0;y<Height;y++)
560 // tileColor start of line.
561 tileColor= tcOrigin + y*2* wTileColor;
562 sint npix= Width;
563 // for all pixels at corner of tessBlock.
564 for(;npix>0; npix--, tileColor+=2, dstLRtc++)
566 *dstLRtc= tileColor->Color565;
570 #endif
573 // compute the TextureFar used for Far dynamic lightmaping.
574 //============
575 // NB: simpler to compute it at generate() time, even if not necessarly needed for near
576 computeTextureFar();
579 // fill texture with Black
580 //============
581 clearLighting();
583 return true;
586 // ***************************************************************************
587 void CPatchDLMContext::clearLighting()
589 // If the srcTexture is not already black.
590 if(!_IsSrcTextureFullBlack)
592 // Reset Lightmap with black.
593 uint count= _LightMap.size();
594 if(count>0)
596 memset(&_LightMap[0], 0, count * sizeof(CRGBA));
599 // Now the src lightmap is fully black
600 _IsSrcTextureFullBlack= true;
605 // ***************************************************************************
607 // TestYoyo: I thought this code was better, but actually, this is not the case
609 static float NL3D_Val1= 1.f;
610 inline void __stdcall fastClamp01(float &x)
612 __asm
614 mov esi, x
615 mov eax, [esi]
617 // clamp to 0.
618 cmp eax, 0x80000001 // set carry if sign bit is set.
619 sbb ecx, ecx // if attDist is negative, ecx==0 , else 0xFFFFFFFF.
620 and eax, ecx // if attDist is negative, eax=0, else unchanged
622 // clamp eax to 1 (NB: now we are sure eax>=0).
623 cmp eax, NL3D_Val1 // set carry if < Val1.
624 sbb ecx, ecx // if < Val1, ecx==0xFFFFFFFF, else 0.
625 and eax, ecx // if < Val1, ecx= eax, else ecx=0
626 not ecx
627 and ecx, NL3D_Val1 // if > Val1, ecx== Val1, else ecx= 0.
628 add eax, ecx // finally, eax= val clamped to 1.
630 // store.
631 mov [esi], eax
635 // faster to do a simple clamp ???
636 inline void fastClamp01(float &x)
638 clamp(x, 0.f, 1.f);
642 // ***************************************************************************
643 void CPatchDLMContext::addPointLightInfluence(const CPatchDLMPointLight &pl)
646 uint nverts= _Vertices.size();
647 nlassert(nverts==_LightMap.size());
649 if(nverts==0)
650 return;
651 CVertex *vert= &_Vertices[0];
654 // precise clip: parse the quadTree of sphere
655 //================
656 uint i, x,y;
657 uint startX, startY, endX, endY;
658 startX= 0xFFFFFFFF;
659 startY= 0xFFFFFFFF;
660 endX= 0;
661 endY= 0;
662 for(i=0;i<_Clusters.size();)
664 // If the sphere intersect pl,
665 if(_Clusters[i].BSphere.intersect(pl.BSphere) )
667 // if this cluster is a leaf, extend start/end
668 if(_Clusters[i].NSkips==0)
670 x= _Clusters[i].X;
671 y= _Clusters[i].Y;
672 startX= min(startX, x);
673 startY= min(startY, y);
674 endX= max(endX, x+1);
675 endY= max(endY, y+1);
677 // go to next cluster (a brother, a parent or a son)
678 i++;
680 else
682 // if this cluster is a leaf, just go to next cluster (a parent or a brother)
683 if(_Clusters[i].NSkips==0)
684 i++;
685 // else, go to next brother or parent (NSkips say how to go)
686 else
687 i+= _Clusters[i].NSkips;
690 // if never intersect, just quit.
691 if(startX==0xFFFFFFFF)
692 return;
694 // get vertices in array to process.
695 startX*=NL_DLM_CLIP_FACTOR;
696 startY*=NL_DLM_CLIP_FACTOR;
697 endX= min(endX*NL_DLM_CLIP_FACTOR+1, Width);
698 endY= min(endY*NL_DLM_CLIP_FACTOR+1, Height);
700 // TestYoyo only.
701 //extern uint YOYO_LandDLCount;
702 //YOYO_LandDLCount+= (endX - startX) * (endY - startY);
704 // process all vertices
705 //================
706 float r,g,b;
707 CRGBA *dst= &_LightMap[0];
708 CVertex *originVert= vert;
709 CRGBA *originDst= dst;
711 // TestYoyo: finally, precache does not seems to impact final result.
712 // precache loading, for better cache use. NB: precache the entire line, ignoring clip result.
713 // Precache only if interesting.
714 //if( (endX - startX)*4>=Width && (endY-startY)>=2)
716 //vert= originVert + startY*Width;
717 //dst= originDst + startY*Width;
718 //uint nPixelLine= (endY-startY)*Width;
719 //CFastMem::precacheBest(vert, nPixelLine * sizeof(CVertex));
720 //CFastMem::precacheBest(dst, nPixelLine * sizeof(CRGBA));
723 // Start 24 precision, for faster compute.
724 OptFastFloorBegin24();
726 // If the pointLight is a spot, compute is more complex/slower
727 if(pl.IsSpot)
729 for(y=startY; y<endY; y++)
731 nverts= endX - startX;
733 vert= originVert + startX + y*Width;
734 dst= originDst + startX + y*Width;
735 for(;nverts>0; nverts--, vert++, dst++)
737 CVector dirToP= vert->Pos - pl.Pos;
738 float dist= dirToP.norm();
739 dirToP/= dist;
741 // compute cos for pl. attenuation
742 float cosSpot= dirToP * pl.Dir;
743 float attSpot= (cosSpot-pl.CosMin) * pl.OOCosDelta;
744 fastClamp01(attSpot);
746 // distance attenuation
747 float attDist= (dist-pl.AttMax) * pl.OOAttDelta;
748 fastClamp01(attDist);
750 // compute diffuse lighting
751 float diff= -(vert->Normal * dirToP);
752 fastClamp01(diff);
754 // compute colors.
755 diff*= attSpot * attDist;
756 r= pl.R*diff;
757 g= pl.G*diff;
758 b= pl.B*diff;
760 CRGBA col;
761 #ifdef NL_OS_MAC
762 // OptFastFloor24 should compiles but it generates an internal compiler error
763 col.R= (uint8)floor(r);
764 col.G= (uint8)floor(g);
765 col.B= (uint8)floor(b);
766 #else
767 // we need to do the 0xff mask or run time type check can break here because sometime r g b are > 255
768 col.R= uint8(OptFastFloor24(r) & 0xff);
769 col.G= uint8(OptFastFloor24(g) & 0xff);
770 col.B= uint8(OptFastFloor24(b) & 0xff);
771 #endif
773 // add to map.
774 #if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM)
775 // Fast AddClamp.
776 __asm
778 mov esi, dst
780 mov al, [esi]dst.R
781 add al, col.R
782 sbb cl, cl
783 or al, cl
784 mov [esi]dst.R, al
786 mov al, [esi]dst.G
787 add al, col.G
788 sbb cl, cl
789 or al, cl
790 mov [esi]dst.G, al
792 mov al, [esi]dst.B
793 add al, col.B
794 sbb cl, cl
795 or al, cl
796 mov [esi]dst.B, al
798 #else
799 // add and clamp to map.
800 dst->addRGBOnly(*dst, col);
801 #endif
805 // else, pointLight with no Spot cone attenuation
806 else
808 // TestYoyo
809 //extern void YOYO_startDLMItCount();
810 //YOYO_startDLMItCount();
812 // Compute lightmap pixels of interest
813 for(y=startY; y<endY; y++)
815 nverts= endX - startX;
817 vert= originVert + startX + y*Width;
818 dst= originDst + startX + y*Width;
819 for(;nverts>0; nverts--, vert++, dst++)
821 CVector dirToP= vert->Pos - pl.Pos;
822 float dist= dirToP.norm();
823 float OODist= 1.0f / dist;
824 dirToP*= OODist;
826 // distance attenuation
827 float attDist= (dist-pl.AttMax) * pl.OOAttDelta;
828 fastClamp01(attDist);
830 // compute diffuse lighting
831 float diff= -(vert->Normal * dirToP);
832 fastClamp01(diff);
834 // compute colors.
835 diff*= attDist;
836 r= pl.R*diff;
837 g= pl.G*diff;
838 b= pl.B*diff;
840 CRGBA col;
841 #ifdef NL_OS_MAC
842 // OptFastFloor24 should compiles but it generates an internal compiler error
843 col.R= (uint8)floor(r);
844 col.G= (uint8)floor(g);
845 col.B= (uint8)floor(b);
846 #else
847 // we need to do the 0xff mask or run time type check can break here because sometime r g b are > 255
848 col.R= uint8(OptFastFloor24(r) & 0xff);
849 col.G= uint8(OptFastFloor24(g) & 0xff);
850 col.B= uint8(OptFastFloor24(b) & 0xff);
851 #endif
852 // add to map.
853 #if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM)
854 // Fast AddClamp.
855 __asm
857 mov esi, dst
859 mov al, [esi]dst.R
860 add al, col.R
861 sbb cl, cl
862 or al, cl
863 mov [esi]dst.R, al
865 mov al, [esi]dst.G
866 add al, col.G
867 sbb cl, cl
868 or al, cl
869 mov [esi]dst.G, al
871 mov al, [esi]dst.B
872 add al, col.B
873 sbb cl, cl
874 or al, cl
875 mov [esi]dst.B, al
877 #else
878 // add and clamp to map.
879 dst->addRGBOnly(*dst, col);
880 #endif
884 // TestYoyo
885 //extern void YOYO_endDLMItCount();
886 //YOYO_endDLMItCount();
889 // Stop 24 bit precision
890 OptFastFloorEnd24();
892 // Src texture is modified, hence it can't be black.
893 //==============
894 _IsSrcTextureFullBlack= false;
898 // ***************************************************************************
899 void CPatchDLMContext::compileLighting(TCompileType compType, CRGBA modulateCte)
901 // If srcTexture is full black, and if dst texture is already full black too, don't need to update dst texture
902 if(! (_IsSrcTextureFullBlack && _IsDstTextureFullBlack) )
904 // if lightMap allocated
905 if(!_LightMap.empty() && _DLMTexture)
907 // If the srcTexture is full black (ie no pointLight influence touch it),
908 if(_IsSrcTextureFullBlack)
910 // reset the texture to full black.
911 _DLMTexture->fillRect(TextPosX, TextPosY, Width, Height, 0);
913 // else the srcTexture is not full black (ie some pointLight influence touch it),
914 else
916 // if must modulate with tileColor
917 if(compType == ModulateTileColor)
919 // a vector can't have negative size
920 //nlassert(_Patch->TileColors.size()>=0);
921 #ifdef NL_DLM_TILE_RES
922 // retrieve userColor pointer.
923 uint16 *tileColor= (uint16*)(&_Patch->TileColors[0]);
924 #else
925 uint16 *tileColor= (uint16*)(&_LowResTileColors[0]);
926 #endif
928 // modulate and fill dest.
929 _DLMTexture->modulateAndfillRect565(TextPosX, TextPosY, Width, Height, &_LightMap[0], tileColor);
931 // else if must modulate with textureFar
932 else if(compType == ModulateTextureFar)
934 // modulate and fill dest.
935 _DLMTexture->modulateAndfillRect8888(TextPosX, TextPosY, Width, Height, &_LightMap[0], &_TextureFar[0]);
937 // else if must modulate with constante
938 else if(compType == ModulateConstant)
940 // modulate and fill dest.
941 _DLMTexture->modulateConstantAndfillRect(TextPosX, TextPosY, Width, Height, &_LightMap[0], modulateCte);
943 // else, no Modulate.
944 else
946 // just copy lightmap to texture
947 _DLMTexture->copyRect(TextPosX, TextPosY, Width, Height, &_LightMap[0]);
953 // copy full black state
954 _IsDstTextureFullBlack= _IsSrcTextureFullBlack;
959 // ***************************************************************************
960 uint CPatchDLMContext::getMemorySize() const
962 uint size= sizeof(CPatchDLMContext);
963 size+= _Vertices.size() * sizeof(CVertex);
964 size+= _LightMap.size() * sizeof(CRGBA);
965 size+= _Clusters.size() * sizeof(CCluster);
966 size+= _TextureFar.size() * sizeof(CRGBA);
967 #ifndef NL_DLM_TILE_RES
968 size+= _LowResTileColors.size() * sizeof(uint16);
969 #endif
971 return size;
975 // ***************************************************************************
976 void CPatchDLMContext::computeTextureFar()
978 // First compute Far at order1 Level (ie 2x2 pixels per tiles).
979 //==================
980 static vector<CRGBA> tileFars;
981 // Get the FarBank from landscape.
982 CTileFarBank &farBank= _Patch->getLandscape()->TileFarBank;
983 // size of the texture.
984 uint os= _Patch->getOrderS();
985 uint ot= _Patch->getOrderT();
986 // resize tmp texture. keep a border of 1 pixel around this texture (for average with border)
987 uint tfWidth= os*2+2;
988 uint tfHeight= ot*2+2;
989 uint tfSize= tfWidth * tfHeight;
990 tileFars.resize(tfSize);
991 CRGBA *dst= &tileFars[0];
993 // default: fill dst with black (for possible non-existing tiles).
994 memset(dst, 0, tfSize*sizeof(CRGBA));
996 // For all tiles.
997 uint x, y;
998 for(y=0; y<ot; y++)
1000 for(x=0;x<os;x++)
1002 // get the tile from patch.
1003 CTileElement &tileElm= _Patch->Tiles[y*os + x];
1005 // For all layers
1006 for(uint l=0; l<3;l++)
1008 uint16 tileId= tileElm.Tile[0];
1009 if (tileId!=NL_TILE_ELM_LAYER_EMPTY)
1011 // Get the read only pointer on the far tile
1012 const CTileFarBank::CTileFar* pTile= farBank.getTile (tileId);
1013 // if exist.
1014 if(pTile && pTile->isFill (CTileFarBank::diffuse))
1016 // get tile element information.
1017 sint nRot= tileElm.getTileOrient(l);
1018 bool is256x256;
1019 uint8 uvOff;
1020 tileElm.getTile256Info(is256x256, uvOff);
1022 // compute src pixel
1023 const CRGBA *srcPixel= pTile->getPixels(CTileFarBank::diffuse, CTileFarBank::order1);
1024 // compute src info, for this tile rot and 256x256 context.
1025 sint srcDeltaX = 0;
1026 sint srcDeltaY = 0;
1027 srcPixel= computeTileFarSrcDeltas(nRot, is256x256, uvOff, srcPixel, srcDeltaX, srcDeltaY);
1029 // compute dst coordinate. start writing at pixel (1,1)
1030 CRGBA *dstPixel= dst + (y*2+1)*tfWidth + x*2+1;
1032 if(l==0)
1034 // copy the tile content to the texture.
1035 copyTileToTexture(srcPixel, srcDeltaX, srcDeltaY, dstPixel, tfWidth);
1037 else
1039 // blend the tile content to the texture.
1040 blendTileToTexture(srcPixel, srcDeltaX, srcDeltaY, dstPixel, tfWidth);
1043 else
1044 // go to next tile.
1045 break;
1047 else
1048 // go to next tile.
1049 break;
1054 /* copy borders pixels from border of current patch
1055 NB: this is not correct, but visually sufficient.
1056 To look on neighbor would be more complex.
1059 // copy lines up and down.
1060 y= tfHeight-1;
1061 for(x=1;x<tfWidth-1;x++)
1063 // copy line 0 from line 1.
1064 dst[0*tfWidth + x]= dst[1*tfWidth + x];
1065 // copy last line from last line-1.
1066 dst[y*tfWidth + x]= dst[(y-1)*tfWidth + x];
1069 // copy column left and right
1070 x= tfWidth-1;
1071 for(y=1;y<tfHeight-1;y++)
1073 // copy column 0 from column 1.
1074 dst[y*tfWidth + 0]= dst[y*tfWidth + 1];
1075 // copy last column from last column-1.
1076 dst[y*tfWidth + x]= dst[y*tfWidth + x-1];
1079 // copy 4 corners
1080 x= tfWidth-1;
1081 y= tfHeight-1;
1082 // top-left corner
1083 dst[0]= dst[1];
1084 // top-right corner
1085 dst[x]= dst[x-1];
1086 // bottom-left corner
1087 dst[y*tfWidth + 0]= dst[y*tfWidth + 1];
1088 // bottom-right corner
1089 dst[y*tfWidth + x]= dst[y*tfWidth + x-1];
1092 // Average to DLM resolution (ie OrderS+1, OrderT+1)
1093 //==================
1094 // resize _TextureFar.
1095 _TextureFar.resize(Width*Height);
1096 CRGBA *src= &tileFars[0];
1097 dst= &_TextureFar[0];
1099 // for all pixels of dst texture.
1100 for(y=0;y<Height;y++)
1102 for(x=0;x<Width;x++, dst++)
1104 // compute coordinate in tileFars.
1105 uint x2, y2;
1106 #ifdef NL_DLM_TILE_RES
1107 x2= x * 2;
1108 y2= y * 2;
1109 #else
1110 // easiest method: sample every 2 tiles.
1111 x2= x * 4;
1112 y2= y * 4;
1113 #endif
1115 // Average the 4 pixels around this tile corner
1116 dst->avg4RGBOnly(src[y2*tfWidth + x2],
1117 src[y2*tfWidth + x2+1],
1118 src[(y2+1)*tfWidth + x2],
1119 src[(y2+1)*tfWidth + x2+1]);
1124 // Modulate result with TileColors.
1125 //==================
1126 // vector-size is always >= 0
1127 //nlassert(_Patch->TileColors.size()>=0);
1128 #ifdef NL_DLM_TILE_RES
1129 // retrieve userColor pointer.
1130 uint16 *tileColor= (uint16*)(&_Patch->TileColors[0]);
1131 #else
1132 uint16 *tileColor= (uint16*)(&_LowResTileColors[0]);
1133 #endif
1135 // For all pixels
1136 dst= &_TextureFar[0];
1137 for(sint n= Width*Height; n>0; n--, dst++, tileColor++)
1139 uint16 tc= *tileColor;
1140 // modulate R.
1141 dst->R= ( (tc>>11) * dst->R)>>5;
1142 // modulate G.
1143 dst->G= (((tc>>5)&63) * dst->G)>>6;
1144 // modulate B.
1145 dst->B= ( (tc&31) * dst->B)>>5;
1152 // ***************************************************************************
1153 const CRGBA *CPatchDLMContext::computeTileFarSrcDeltas(sint nRot, bool is256x256, uint8 uvOff, const CRGBA *srcPixel, sint &srcDeltaX, sint &srcDeltaY)
1155 // NB: code copied from CTextureFar::rebuildRectangle()
1157 // The tileSize at order1 is 2.
1158 uint tileSize= 2;
1160 // Source size
1161 sint sourceSize;
1163 // Source offset (for 256)
1164 uint sourceOffset=0;
1166 // 256 ?
1167 if (is256x256)
1169 // On the left ?
1170 if (uvOff&0x02)
1171 sourceOffset+=tileSize;
1173 // On the bottom ?
1174 if ((uvOff==1)||(uvOff==2))
1175 sourceOffset+=2*tileSize*tileSize;
1177 // Yes, 256
1178 sourceSize=tileSize<<1;
1180 else
1182 // No, 128
1183 sourceSize=tileSize;
1186 // Compute offset and deltas
1187 switch (nRot)
1189 case 0:
1190 // Source pointers
1191 srcPixel= srcPixel+sourceOffset;
1193 // Source delta
1194 srcDeltaX=1;
1195 srcDeltaY=sourceSize;
1196 break;
1197 case 1:
1199 // Source pointers
1200 uint newOffset=sourceOffset+(tileSize-1);
1201 srcPixel=srcPixel+newOffset;
1203 // Source delta
1204 srcDeltaX=sourceSize;
1205 srcDeltaY=-1;
1207 break;
1208 case 2:
1210 // Destination pointer
1211 uint newOffset=sourceOffset+(tileSize-1)*sourceSize+tileSize-1;
1212 srcPixel=srcPixel+newOffset;
1214 // Source delta
1215 srcDeltaX=-1;
1216 srcDeltaY=-sourceSize;
1218 break;
1219 case 3:
1221 // Destination pointer
1222 uint newOffset=sourceOffset+(tileSize-1)*sourceSize;
1223 srcPixel=srcPixel+newOffset;
1225 // Source delta
1226 srcDeltaX=-sourceSize;
1227 srcDeltaY=1;
1229 break;
1232 return srcPixel;
1236 // ***************************************************************************
1237 void CPatchDLMContext::copyTileToTexture(const CRGBA *srcPixel, sint srcDeltaX, sint srcDeltaY, CRGBA *dstPixel, uint dstStride)
1239 // copy the 2x2 tile to the texture.
1241 // first line.
1242 dstPixel[0]= srcPixel[0];
1243 dstPixel[1]= srcPixel[srcDeltaX];
1244 // second line.
1245 dstPixel[0+dstStride]= srcPixel[srcDeltaY];
1246 dstPixel[1+dstStride]= srcPixel[srcDeltaY+srcDeltaX];
1249 // ***************************************************************************
1250 void CPatchDLMContext::blendTileToTexture(const CRGBA *srcPixel, sint srcDeltaX, sint srcDeltaY, CRGBA *dstPixel, uint dstStride)
1252 // blend the 2x2 tile with the texture.
1253 CRGBA *dst;
1254 CRGBA src;
1256 // first line.
1257 dst= &dstPixel[0]; src= srcPixel[0];
1258 dst->blendFromuiRGBOnly(*dst, src, src.A);
1260 dst= &dstPixel[1]; src= srcPixel[srcDeltaX];
1261 dst->blendFromuiRGBOnly(*dst, src, src.A);
1263 // second line.
1264 dst= &dstPixel[0+dstStride]; src= srcPixel[srcDeltaY];
1265 dst->blendFromuiRGBOnly(*dst, src, src.A);
1267 dst= &dstPixel[1+dstStride]; src= srcPixel[srcDeltaY+srcDeltaX];
1268 dst->blendFromuiRGBOnly(*dst, src, src.A);
1272 } // NL3D