1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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"
32 using namespace NLMISC
;
41 // ***************************************************************************
42 // ***************************************************************************
43 // ***************************************************************************
46 // ***************************************************************************
47 void CPatchDLMPointLight::compile(const CPointLight
&pl
, NLMISC::CRGBA landDiffMat
, float maxAttEnd
)
49 nlassert(maxAttEnd
>0);
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);
56 IsSpot
= pl
.getType() == CPointLight::SpotLight
;
57 Pos
= pl
.getPosition();
58 Dir
= pl
.getSpotDirection();
60 // compute spot params
63 CosMax
= cosf(pl
.getSpotAngleBegin());
64 CosMin
= cosf(pl
.getSpotAngleEnd());
68 // with tesse Values, we have always (cosSpot-CosMin) * OOCosDelta > 1.0f
72 OOCosDelta
= 1.f
/ (CosMax
-CosMin
);
75 AttMax
= pl
.getAttenuationEnd();
76 AttMin
= pl
.getAttenuationBegin();
77 // infinite pointLight?
81 AttMin
= maxAttEnd
*0.99f
;
84 else if(AttMax
>maxAttEnd
)
87 AttMin
= min(AttMin
, maxAttEnd
*0.99f
);
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
99 BSphere
.Radius
= AttMax
;
100 // The bbox englobe the sphere.
102 BBox
.setHalfSize(CVector(AttMax
, AttMax
, AttMax
));
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
));
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!!
131 radius2
= max(radius2
, dccs
);
134 // Then take the center which gives the smaller sphere
137 BSphere
.Center
= Pos
+ (Dir
*0.5f
*AttMax
);
138 // radius1 E [0,1], must take real size.
139 BSphere
.Radius
= radius1
* AttMax
;
143 BSphere
.Center
= Pos
+ (Dir
*CosMin
*AttMax
);
144 // radius2 E [0,1], must take real size.
145 BSphere
.Radius
= radius2
* AttMax
;
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()
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
185 _DLMTexture
->releaseLightMap(TextPosX
, TextPosY
);
191 // remove it from list.
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
202 // start to clip at tessBlockLevel (same as dlm map precision)
203 #define NL_DLM_CLIP_FACTOR 1
206 #define NL_DLM_CLIP_NUM_LEVEL 3
208 // ***************************************************************************
209 bool CPatchDLMContext::generate(CPatch
*patch
, CTextureDLM
*textureDLM
, CPatchDLMContextList
*ctxList
)
212 nlassert(textureDLM
);
215 // keep info on patch/landscape.
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;
228 // get coord at cornes of tessBlocks
229 Width
= (_Patch
->getOrderS()/2)+1;
230 Height
= (_Patch
->getOrderT()/2)+1;
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)
241 // If the lightmap is correclty allocated in the global texture, compute UVBias.
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();
252 // Build UVBias such that the UVs point to Black
253 // NB: TextureDLM ensure that point (MaxX,MaxY) of texture is black.
260 // TestYoyo: to see lightmap usage in the big texture
261 /*DLMUScale= _Patch->getOrderS();
262 DLMVScale= _Patch->getOrderT();
267 // Bound 8bits UV for Vegetable. This is to ensure vegetable Dlm UVs won't peek in neighbor lightmaps.
270 tmpU
= (sint
)ceil ( (DLMUBias
) * 255 );
274 tmpU
= (sint
)floor( (DLMUBias
+DLMUScale
) * 255 );
275 clamp(tmpU
, (sint
)MinU8
, 255);
278 tmpV
= (sint
)ceil ( (DLMVBias
) * 255 );
282 tmpV
= (sint
)floor( (DLMVBias
+DLMVScale
) * 255 );
283 clamp(tmpV
, (sint
)MinV8
, 255);
287 // Allocate RAM Lightmap
288 _LightMap
.resize(Width
*Height
);
290 // generate Vertices: pos and normals
291 _Vertices
.resize(Width
*Height
);
293 float ds
= 1.0f
/ (Width
-1);
294 float dt
= 1.0f
/ (Height
-1);
295 // eval all the patch.
298 for(y
=0; y
<Height
; y
++, t
+=dt
)
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();
307 vert
.Pos
= bpatch
->eval(s
, t
);
309 vert
.Normal
= bpatch
->evalNormal(s
, t
);
313 // Build bounding Spheres QuadTree
316 // Size of the cluster array (at level 0)
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
);
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
);
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
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
);
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
);
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
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
];
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
;
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
403 // Build a bbox for 4 (or 2) children clusters
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
);
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
;
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
;
440 nextBsx
= max(1U, nextBsx
/2);
441 nextBsy
= max(1U, nextBsy
/2);
447 // Resize clusters with size according to all levels
448 _Clusters
.resize(finalClusterSize
);
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.
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
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"
495 _Clusters
[iDstCluster
].NSkips
= tmpClusterNumToSkip
[curLevel
];
501 // If not Leaf level, recurs. First pass, use curLevel params (tmpClusterX...)
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
]++;
518 if(tmpClusterX
[curLevel
] >= tmpClusterXMax
[curLevel
])
521 tmpClusterX
[curLevel
]= tmpClusterXMin
[curLevel
];
523 tmpClusterY
[curLevel
]++;
527 // If not Leaf level, recurs. Second pass, after tmpClusterX and tmpClusterY of curLevel are changed
530 // descend in hierarchy. (recurs)
536 // All dst clusters must have been filled
537 nlassert(iDstCluster
== _Clusters
.size());
540 // PreProcess Patch TileColors.
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
;
563 // for all pixels at corner of tessBlock.
564 for(;npix
>0; npix
--, tileColor
+=2, dstLRtc
++)
566 *dstLRtc
= tileColor
->Color565
;
573 // compute the TextureFar used for Far dynamic lightmaping.
575 // NB: simpler to compute it at generate() time, even if not necessarly needed for near
579 // fill texture with Black
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();
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)
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
627 and ecx, NL3D_Val1 // if > Val1, ecx== Val1, else ecx= 0.
628 add eax, ecx // finally, eax= val clamped to 1.
635 // faster to do a simple clamp ???
636 inline void fastClamp01(float &x
)
642 // ***************************************************************************
643 void CPatchDLMContext::addPointLightInfluence(const CPatchDLMPointLight
&pl
)
646 uint nverts
= _Vertices
.size();
647 nlassert(nverts
==_LightMap
.size());
651 CVertex
*vert
= &_Vertices
[0];
654 // precise clip: parse the quadTree of sphere
657 uint startX
, startY
, endX
, endY
;
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)
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)
682 // if this cluster is a leaf, just go to next cluster (a parent or a brother)
683 if(_Clusters
[i
].NSkips
==0)
685 // else, go to next brother or parent (NSkips say how to go)
687 i
+= _Clusters
[i
].NSkips
;
690 // if never intersect, just quit.
691 if(startX
==0xFFFFFFFF)
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
);
701 //extern uint YOYO_LandDLCount;
702 //YOYO_LandDLCount+= (endX - startX) * (endY - startY);
704 // process all vertices
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
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();
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
);
755 diff
*= attSpot
* attDist
;
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
);
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);
774 #if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM)
799 // add and clamp to map.
800 dst
->addRGBOnly(*dst
, col
);
805 // else, pointLight with no Spot cone attenuation
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
;
826 // distance attenuation
827 float attDist
= (dist
-pl
.AttMax
) * pl
.OOAttDelta
;
828 fastClamp01(attDist
);
830 // compute diffuse lighting
831 float diff
= -(vert
->Normal
* dirToP
);
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
);
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);
853 #if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM)
878 // add and clamp to map.
879 dst
->addRGBOnly(*dst
, col
);
885 //extern void YOYO_endDLMItCount();
886 //YOYO_endDLMItCount();
889 // Stop 24 bit precision
892 // Src texture is modified, hence it can't be black.
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),
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]);
925 uint16
*tileColor
= (uint16
*)(&_LowResTileColors
[0]);
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.
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
);
975 // ***************************************************************************
976 void CPatchDLMContext::computeTextureFar()
978 // First compute Far at order1 Level (ie 2x2 pixels per tiles).
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
));
1002 // get the tile from patch.
1003 CTileElement
&tileElm
= _Patch
->Tiles
[y
*os
+ x
];
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
);
1014 if(pTile
&& pTile
->isFill (CTileFarBank::diffuse
))
1016 // get tile element information.
1017 sint nRot
= tileElm
.getTileOrient(l
);
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.
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;
1034 // copy the tile content to the texture.
1035 copyTileToTexture(srcPixel
, srcDeltaX
, srcDeltaY
, dstPixel
, tfWidth
);
1039 // blend the tile content to the texture.
1040 blendTileToTexture(srcPixel
, srcDeltaX
, srcDeltaY
, dstPixel
, tfWidth
);
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.
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
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];
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.
1106 #ifdef NL_DLM_TILE_RES
1110 // easiest method: sample every 2 tiles.
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]);
1132 uint16
*tileColor
= (uint16
*)(&_LowResTileColors
[0]);
1136 dst
= &_TextureFar
[0];
1137 for(sint n
= Width
*Height
; n
>0; n
--, dst
++, tileColor
++)
1139 uint16 tc
= *tileColor
;
1141 dst
->R
= ( (tc
>>11) * dst
->R
)>>5;
1143 dst
->G
= (((tc
>>5)&63) * dst
->G
)>>6;
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.
1163 // Source offset (for 256)
1164 uint sourceOffset
=0;
1171 sourceOffset
+=tileSize
;
1174 if ((uvOff
==1)||(uvOff
==2))
1175 sourceOffset
+=2*tileSize
*tileSize
;
1178 sourceSize
=tileSize
<<1;
1183 sourceSize
=tileSize
;
1186 // Compute offset and deltas
1191 srcPixel
= srcPixel
+sourceOffset
;
1195 srcDeltaY
=sourceSize
;
1200 uint newOffset
=sourceOffset
+(tileSize
-1);
1201 srcPixel
=srcPixel
+newOffset
;
1204 srcDeltaX
=sourceSize
;
1210 // Destination pointer
1211 uint newOffset
=sourceOffset
+(tileSize
-1)*sourceSize
+tileSize
-1;
1212 srcPixel
=srcPixel
+newOffset
;
1216 srcDeltaY
=-sourceSize
;
1221 // Destination pointer
1222 uint newOffset
=sourceOffset
+(tileSize
-1)*sourceSize
;
1223 srcPixel
=srcPixel
+newOffset
;
1226 srcDeltaX
=-sourceSize
;
1236 // ***************************************************************************
1237 void CPatchDLMContext::copyTileToTexture(const CRGBA
*srcPixel
, sint srcDeltaX
, sint srcDeltaY
, CRGBA
*dstPixel
, uint dstStride
)
1239 // copy the 2x2 tile to the texture.
1242 dstPixel
[0]= srcPixel
[0];
1243 dstPixel
[1]= srcPixel
[srcDeltaX
];
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.
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
);
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
);