Change Encyclo button name and macros icon
[ryzomcore.git] / nel / src / 3d / shadow_map_manager.cpp
blob174ecd580f915c5c887e6700a50b68c2ba780a79
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) 2013-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"
21 #include "nel/3d/shadow_map_manager.h"
22 #include "nel/misc/aabbox.h"
23 #include "nel/3d/driver.h"
24 #include "nel/3d/scene.h"
25 #include "nel/3d/viewport.h"
26 #include "nel/3d/scissor.h"
27 #include "nel/3d/dru.h"
28 #include "nel/3d/texture_mem.h"
29 #include "nel/3d/visual_collision_manager.h"
30 #include "nel/misc/hierarchical_timer.h"
31 #include "nel/misc/fast_floor.h"
34 using namespace NLMISC;
35 using namespace std;
37 #ifdef DEBUG_NEW
38 #define new DEBUG_NEW
39 #endif
41 namespace NL3D {
44 // ***************************************************************************
45 // easineasout
46 static inline float easeInEaseOut(float x)
48 float y;
49 // cubic such that f(0)=0, f'(0)=0, f(1)=1, f'(1)=0.
50 float x2=x*x;
51 float x3=x2*x;
52 y= -2*x3 + 3*x2;
53 return y;
57 // ***************************************************************************
58 CShadowMapManager::CShadowMapManager()
60 uint i;
62 // For Texture profiling
63 _TextureCategory= new ITexture::CTextureCategory("SHADOW MANAGER");
65 setQuadGridSize(NL3D_SMM_QUADGRID_SIZE, NL3D_SMM_QUADCELL_SIZE);
66 _ShadowCasters.reserve(256);
67 _GenerateShadowCasters.reserve(256);
68 _PolySmooth= true;
70 // **** Setup Fill
71 _FillQuads.setVertexFormat(CVertexBuffer::PositionFlag);
72 _FillMaterial.initUnlit();
73 _FillMaterial.setColor(CRGBA(0,0,0,0));
74 _FillMaterial.setZWrite(false);
75 _FillMaterial.setZFunc(CMaterial::always);
76 _FillMaterial.setDoubleSided(true);
77 _FillQuads.setPreferredMemory(CVertexBuffer::RAMVolatile, true);
79 // **** Setup Blur
80 _BlurQuads.setVertexFormat(CVertexBuffer::PositionFlag |
81 CVertexBuffer::TexCoord0Flag |
82 CVertexBuffer::TexCoord1Flag |
83 CVertexBuffer::TexCoord2Flag |
84 CVertexBuffer::TexCoord3Flag);
85 _BlurQuads.setPreferredMemory(CVertexBuffer::RAMVolatile, true);
87 // Only 2 quads are used to blur
88 _BlurQuads.setNumVertices(8);
89 for (i=0;i<2;i++)
91 _BlurMaterial[i].initUnlit();
92 _BlurMaterial[i].setColor(CRGBA::White);
93 _BlurMaterial[i].setZWrite(false);
94 _BlurMaterial[i].setZFunc(CMaterial::always);
95 _BlurMaterial[i].setDoubleSided(true);
96 // Setup The Blur. NB: it will take advantage of Max 4 texture driver support, but will still
97 // work with 2 or 3 (less beautifull).
98 uint j;
99 for(j=1;j<4;j++)
101 _BlurMaterial[i].texEnvOpRGB(j, CMaterial::InterpolateConstant);
102 _BlurMaterial[i].texEnvArg0RGB(j, CMaterial::Texture, CMaterial::SrcColor);
103 _BlurMaterial[i].texEnvArg1RGB(j, CMaterial::Previous, CMaterial::SrcColor);
104 _BlurMaterial[i].texEnvOpAlpha(j, CMaterial::InterpolateConstant);
105 _BlurMaterial[i].texEnvArg0Alpha(j, CMaterial::Texture, CMaterial::SrcAlpha);
106 _BlurMaterial[i].texEnvArg1Alpha(j, CMaterial::Previous, CMaterial::SrcAlpha);
108 // Factor for Stage so the sum is 1.
109 _BlurMaterial[i].texConstantColor(1, CRGBA(128,128,128,128)); // factor= 1/2
110 _BlurMaterial[i].texConstantColor(2, CRGBA(85,85,85,85)); // factor= 1/3
111 _BlurMaterial[i].texConstantColor(3, CRGBA(64,64,64,64)); // factor= 1/4
114 _BlurTextureW= 0;
115 _BlurTextureH= 0;
117 // *** Setup copy
118 _CopyQuads.setVertexFormat (CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag);
119 _CopyQuads.setNumVertices(4);
120 _CopyQuads.setPreferredMemory(CVertexBuffer::RAMVolatile, true);
121 CVertexBufferReadWrite vba;
122 _CopyQuads.lock (vba);
123 vba.setVertexCoord (0, CVector (0, 0, 0));
124 vba.setVertexCoord (1, CVector (1, 0, 0));
125 vba.setVertexCoord (2, CVector (1, 0, 1));
126 vba.setVertexCoord (3, CVector (0, 0, 1));
128 // Copy material
129 _CopyMaterial.initUnlit();
130 _CopyMaterial.setColor(CRGBA::White);
131 _CopyMaterial.setZWrite(false);
132 _CopyMaterial.setZFunc(CMaterial::always);
133 _CopyMaterial.setDoubleSided(true);
134 _CopyMaterial.setBlend (false);
135 _CopyMaterial.setAlphaTest (false);
136 _CopyMaterial.setBlendFunc (CMaterial::one, CMaterial::zero);
138 // **** Setup Receiving
140 // Setup the clamp texture.
141 const uint clampTextSize= 512;
142 const uint clampNearFadeSize= 32;
143 const uint clampFarFadeSize= 128;
144 uint textMemSize= 4*clampTextSize*1;
145 // Fill mem
146 uint8 *tmpMem= new uint8[textMemSize];
147 memset(tmpMem, 255, textMemSize);
148 for(i=0;i<clampNearFadeSize;++i)
150 float f= (float)i/clampNearFadeSize;
151 f= easeInEaseOut(f);
152 tmpMem[4*i+3]= uint8(255.f*f);
154 for(i=0;i<clampFarFadeSize;++i)
156 float f= (float)i/clampFarFadeSize;
157 f= easeInEaseOut(f);
158 tmpMem[4*(clampTextSize-i-1)+3]= uint8(255.f*f);
160 // build the texture
161 _ClampTexture = new CTextureMem (tmpMem, 4*clampTextSize*1, true, false, clampTextSize, 1);
162 _ClampTexture->setWrapS (ITexture::Clamp);
163 _ClampTexture->setWrapT (ITexture::Clamp);
164 _ClampTexture->setFilterMode (ITexture::Linear, ITexture::LinearMipMapOff);
165 _ClampTexture->generate();
166 _ClampTexture->setReleasable (false);
167 // For Texture Profiling
168 _ClampTexture->setTextureCategory(_TextureCategory);
170 // init material
171 _ReceiveShadowMaterial.initUnlit();
172 _ReceiveShadowMaterial.setBlend(true);
173 _ReceiveShadowMaterial.setBlendFunc(CMaterial::zero, CMaterial::invsrccolor);
174 _ReceiveShadowMaterial.setZWrite(false);
175 // FillRate Optim
176 _ReceiveShadowMaterial.setAlphaTest(true);
177 _ReceiveShadowMaterial.setAlphaTestThreshold(0.01f);
179 // ---- Stage 0. Project the ShadowMap. Blend the color between ShadowColor and White.
180 // setup texture coord gen
181 _ReceiveShadowMaterial.enableUserTexMat(0, true);
182 _ReceiveShadowMaterial.setTexCoordGen(0, true);
183 _ReceiveShadowMaterial.setTexCoordGenMode(0, CMaterial::TexCoordGenObjectSpace);
184 // Setup the stage so we interpolate ShadowColor and White (according to shadowmap alpha)
185 // nico : with D3D driver, limitation of the number of per stage constant (Only 1 if diffuse is used), so do a blend between inv diffuse & black (instead of diffuse & white), which resolve to a modulate between
186 // source alpha & inverse diffuse. then invert result at subsequent stage
187 _ReceiveShadowMaterial.texEnvOpRGB(0, CMaterial::Modulate);
188 _ReceiveShadowMaterial.texEnvArg0RGB(0, CMaterial::Diffuse, CMaterial::InvSrcColor);
189 _ReceiveShadowMaterial.texEnvArg1RGB(0, CMaterial::Texture, CMaterial::SrcAlpha);
190 // Take Alpha for AlphaTest only.
191 _ReceiveShadowMaterial.texEnvOpAlpha(0, CMaterial::Replace);
192 _ReceiveShadowMaterial.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);
194 // ---- Stage 1. "Modulate" by Clamp Texture. Blend the color between stage0 color and White.
195 // setup texture coord gen
196 _ReceiveShadowMaterial.enableUserTexMat(1, true);
197 _ReceiveShadowMaterial.setTexCoordGen(1, true);
198 _ReceiveShadowMaterial.setTexCoordGenMode(1, CMaterial::TexCoordGenObjectSpace);
199 _ReceiveShadowMaterial.setTexture(1, _ClampTexture);
200 // Setup the stage so we interpolate Shadow and White (according to clamp alpha)
201 _ReceiveShadowMaterial.texEnvOpRGB(1, CMaterial::Modulate);
202 _ReceiveShadowMaterial.texEnvArg0RGB(1, CMaterial::Previous, CMaterial::SrcColor); // Color is inverted before the blend
203 _ReceiveShadowMaterial.texEnvArg1RGB(1, CMaterial::Texture, CMaterial::SrcAlpha);
204 // Take Alpha for AlphaTest only. (take 1st texture alpha...)
205 _ReceiveShadowMaterial.texEnvOpAlpha(0, CMaterial::Replace);
206 _ReceiveShadowMaterial.texEnvArg0Alpha(0, CMaterial::Previous, CMaterial::SrcAlpha);
208 // **** Setup Casting
209 _CasterShadowMaterial.initUnlit();
210 _CasterShadowMaterial.setColor(CRGBA::White);
211 _CasterShadowMaterial.setZWrite(false);
212 _CasterShadowMaterial.setZFunc(CMaterial::always);
213 _CasterShadowMaterial.setDoubleSided(true);
214 // Alpha Polygon coverage accumulate, for polygon smoothing
215 _CasterShadowMaterial.setBlend(true);
216 _CasterShadowMaterial.setBlendFunc(CMaterial::one, CMaterial::one);
218 _BlurQuads.setName("CShadowMapManager::_BlurQuads");
219 _FillQuads.setName("CShadowMapManager::_FillQuads");
220 _CopyQuads.setName("CShadowMapManager::_CopyQuads");
223 // ***************************************************************************
224 CShadowMapManager::~CShadowMapManager()
226 clearAllShadowCasters();
229 // ***************************************************************************
230 void CShadowMapManager::setQuadGridSize(uint size, float cellSize)
232 _ShadowReceiverGrid.create(size, cellSize);
235 // ***************************************************************************
236 void CShadowMapManager::addShadowCaster(CTransform *model)
238 _ShadowCasters.push_back(model);
241 // ***************************************************************************
242 void CShadowMapManager::addShadowReceiver(CTransform *model)
244 CAABBox bb;
245 model->getReceiverBBox(bb);
247 _ShadowReceiverGrid.insert(bb.getMin(), bb.getMax(), model);
250 // ***************************************************************************
251 void CShadowMapManager::renderGenerate(CScene *scene)
253 H_AUTO( NL3D_ShadowManager_Generate );
255 // Each frame, do a small garbage collector for unused free textures.
256 garbageShadowTextures(scene);
258 IDriver *driverForShadowGeneration= scene->getRenderTrav().getAuxDriver();
259 nlassert(driverForShadowGeneration);
260 CSmartPtr<NL3D::ITexture> previousRenderTarget = driverForShadowGeneration->getRenderTarget();
262 // Init
263 // ********
264 uint32 wndW= _BlurTextureW, wndH= _BlurTextureH;
265 // get some text/screen size.
266 driverForShadowGeneration->getWindowSize(wndW, wndH);
267 uint baseTextureSize= scene->getShadowMapTextureSize();
268 // Minimize the Dest Texture size, so the blurTexture don't get too heavy in VRAM.
269 uint32 textDestW= min(wndW, (uint32)NL3D_SMM_MAX_TEXTDEST_SIZE);
270 uint32 textDestH= min(wndH, (uint32)NL3D_SMM_MAX_TEXTDEST_SIZE);
272 // if not needed or if not possible, exit. test for wndSize is also important when window is minimized
273 if( _ShadowCasters.empty() ||
274 textDestW<baseTextureSize || textDestH<baseTextureSize)
276 clearAllShadowCasters();
277 return;
280 // If Needed to project some ShadowCaster, but none to compute this frame, quit.
281 if( _GenerateShadowCasters.empty() )
283 // But here don't reset since the renderProject() will do job
284 return;
287 // get the number of shadowMap compute we can do in one screen.
288 uint numTextW= textDestW/baseTextureSize;
289 uint numTextH= textDestH/baseTextureSize;
290 if(!isPowerOf2(numTextW))
291 numTextW= raiseToNextPowerOf2(numTextW)/2;
292 if(!isPowerOf2(numTextH))
293 numTextH= raiseToNextPowerOf2(numTextH)/2;
294 // the max shadow casters we can do in 1 screen pass.
295 uint maxSCPerPass= numTextW * numTextH;
297 // compute vp float size.
298 float vpWidth= (float)baseTextureSize / (float)(numTextW*baseTextureSize);
299 float vpHeight= (float)baseTextureSize / (float)(numTextH*baseTextureSize);
302 // Create / Update the Blur Texture
303 updateBlurTexture(*driverForShadowGeneration, numTextW * baseTextureSize, numTextH * baseTextureSize);
306 // Do NPass if a screen is not sufficient to render all shadow maps...
307 // ********
309 // bkup driver state
310 CViewport bkupViewport;
311 driverForShadowGeneration->getViewport(bkupViewport);
312 bool bkupFog= driverForShadowGeneration->fogEnabled();
314 // setup some state
315 driverForShadowGeneration->enableFog(false);
316 // Allow Writing on alpha only. => don't write on RGB objects!
317 driverForShadowGeneration->setColorMask(false, false, false, true);
319 uint numSC= (uint)_GenerateShadowCasters.size();
320 uint baseSC= 0;
321 while(numSC>0)
323 uint numPassSC= min(maxSCPerPass, numSC);
324 // number of line including the last line if not empty
325 uint numTotalLine= (numPassSC+numTextW-1)/numTextW;
326 // number of column.
327 uint numTotalCol= (numPassSC<numTextW)?numPassSC:numTextW;
329 // Render to the blur texture
330 driverForShadowGeneration->setRenderTarget (_BlurTexture[0], 0, 0, numTotalCol*baseTextureSize, numTotalLine*baseTextureSize);
332 uint textX, textY;
333 uint i;
335 // Render All Shadow Map
336 // ********
338 // Render the polygons with Smooth Anti-Alias. Less jittering for little performance overcost
339 if(_PolySmooth)
340 driverForShadowGeneration->enablePolygonSmoothing(true);
342 textX=0;
343 textY=0;
344 for(i=0;i<numPassSC;i++)
346 // get the transform to compute shadow map.
347 CTransform *sc= _GenerateShadowCasters[baseSC+i];
349 // select the shadow direction
350 CVector lightDir;
351 computeShadowDirection(scene, sc, lightDir);
353 // setup viewport to render to
354 CViewport vp;
355 vp.init(textX*baseTextureSize/(float)_BlurTextureW, textY*baseTextureSize/(float)_BlurTextureH, vpWidth, vpHeight);
356 driverForShadowGeneration->setupViewport(vp);
358 // TODO_SHADOW: optim: one big erase per pass, but just bbox needed (according to number of SC to render)
359 // do a siccor or prefer do a polygon clear?
360 CScissor sic;
361 sic.init(textX*baseTextureSize/(float)_BlurTextureW, textY*baseTextureSize/(float)_BlurTextureH, vpWidth, vpHeight);
362 driverForShadowGeneration->setupScissor(sic);
363 driverForShadowGeneration->clear2D(CRGBA(0,0,0,0));
365 // render to screen
366 sc->generateShadowMap(lightDir);
368 // next text
369 textX++;
370 if(textX==numTextW)
372 textX= 0;
373 textY++;
377 // Restore
378 if(_PolySmooth)
379 driverForShadowGeneration->enablePolygonSmoothing(false);
381 // For Subsequent operations, setup a full viewport and a "Screen Frustum"
382 CScissor sic;
383 sic.initFullScreen();
384 // TODO_SHADOW: optim: need scissor?
385 driverForShadowGeneration->setupScissor(sic);
386 CViewport vp;
387 vp.initFullScreen();
388 driverForShadowGeneration->setupViewport(vp);
389 driverForShadowGeneration->setFrustum(0, (float)_BlurTextureW, 0, (float)_BlurTextureH, -1,1,false);
390 driverForShadowGeneration->setupViewMatrix(CMatrix::Identity);
391 driverForShadowGeneration->setupModelMatrix(CMatrix::Identity);
393 // Ensure the One pixel black security on texture border
394 fillBlackBorder(driverForShadowGeneration, numPassSC, numTextW, numTextH, baseTextureSize);
396 // Blur.
397 // ********
398 uint numBlur= scene->getShadowMapBlurSize();
399 clamp(numBlur, 0U, 3U);
400 uint blurTarget = 0;
401 for(i=0;i<numBlur;i++)
403 // Set the blur texture target
404 blurTarget = (i+1)&1;
405 const uint blurSource = i&1;
406 driverForShadowGeneration->setRenderTarget (_BlurTexture[blurTarget], 0, 0, numTotalCol*baseTextureSize, numTotalLine*baseTextureSize);
408 // blur
409 applyFakeGaussianBlur(driverForShadowGeneration, numPassSC, numTextW, numTextH, baseTextureSize, blurSource);
411 // Ensure the One pixel black security on texture border
412 fillBlackBorder(driverForShadowGeneration, numPassSC, numTextW, numTextH, baseTextureSize);
415 // Copy the last blur texture
416 _CopyMaterial.setTexture(0, _BlurTexture[blurTarget]);
418 // Store Screen in ShadowMaps
419 // ********
420 textX=0;
421 textY=0;
422 for(i=0;i<numPassSC;i++)
424 // get the transform to compute shadow map.
425 CTransform *sc= _GenerateShadowCasters[baseSC+i];
426 CShadowMap *sm= sc->getShadowMap();
427 if(sm)
429 ITexture *text= sm->getTexture();
430 if(text)
432 uint bts= baseTextureSize;
434 // todo hulud : Try the temporary buffer trick (openGL)
435 //if (!driverForShadowGeneration->copyTargetToTexture (text, 0, 0, textX*bts, textY*bts, bts, bts))
437 // Copy the texture
438 driverForShadowGeneration->setRenderTarget (text, 0, 0, bts, bts);
439 driverForShadowGeneration->clear2D (CRGBA(0,0,0,0));
441 // Viewport is already fullscreen, set the frustum
442 driverForShadowGeneration->setFrustum(0, 1, 0, 1, -1,1,false);
444 // Set the vertex buffer UV
446 CVertexBufferReadWrite vba;
447 _CopyQuads.lock (vba);
448 const float u= (float)(textX*bts)*_BlurTextureOOW;
449 const float v= (float)(textY*bts)*_BlurTextureOOH;
450 const float width= (float)bts*_BlurTextureOOW;
451 const float height= (float)bts*_BlurTextureOOH;
452 vba.setTexCoord (0, 0, u, v);
453 vba.setTexCoord (1, 0, u+width, v);
454 vba.setTexCoord (2, 0, u+width, v+height);
455 vba.setTexCoord (3, 0, u, v+height);
458 // Vertex buffer
459 driverForShadowGeneration->activeVertexBuffer (_CopyQuads);
461 CScissor sic;
462 sic.initFullScreen();
463 // TODO_SHADOW: optim: need scissor?
464 driverForShadowGeneration->setupScissor(sic);
466 driverForShadowGeneration->setupViewMatrix(CMatrix::Identity);
467 driverForShadowGeneration->setupModelMatrix(CMatrix::Identity);
469 // Render the shadow in the final shadow texture
470 vp.init (0, 0, 1, 1);
471 driverForShadowGeneration->setupViewport(vp);
472 driverForShadowGeneration->renderRawQuads (_CopyMaterial, 0, 1);
474 // Set default render target
475 driverForShadowGeneration->setRenderTarget (NULL);
478 // Indicate to the ShadowMap that we have updated his Texture
479 sm->LastGenerationFrame= scene->getNumRender();
483 // next text
484 textX++;
485 if(textX==numTextW)
487 textX= 0;
488 textY++;
493 // next screen pass.
494 baseSC+= numPassSC;
495 numSC-= numPassSC;
498 // Set default render target
499 driverForShadowGeneration->setRenderTarget (previousRenderTarget);
501 // Allow Writing on all.
502 driverForShadowGeneration->setColorMask(true, true, true, true);
503 // Restore driver state. (do it here because driverForShadowGeneration may be the main screen).
504 driverForShadowGeneration->setupViewport(bkupViewport);
505 driverForShadowGeneration->enableFog(bkupFog);
506 // TODO_SHADOW: optim need scissor?
507 CScissor sic;
508 sic.initFullScreen();
509 driverForShadowGeneration->setupScissor(sic);
511 // ensure the Scene Driver has correct matrix setup (in common case where AuxDriver == Std Driver)
512 scene->getRenderTrav().setupDriverCamera();
515 // Clear ShadowCaster Generation
516 clearGenerateShadowCasters();
519 // ***************************************************************************
520 void CShadowMapManager::renderProject(CScene *scene)
522 // if not needed exit. NB renderGenerate() must have been called before.
523 if( _ShadowCasters.empty() )
525 return;
529 // Project ShadowMap on receivers.
530 // ********
532 H_AUTO( NL3D_ShadowManager_Project );
535 /* Fog Case: Since we do a modulate, we don't want to modulate the fog color with himself.
536 Instead, if the shadowed pixel is in full fog, we have to modulate him with Blac (modulate with INVERSE-source color, actually ...)
537 => replace fog color with black temporarily
539 IDriver *driver= scene->getRenderTrav().getDriver();
540 CRGBA bkupFogColor= driver->getFogColor();
543 driver->setupFog(driver->getFogStart(), driver->getFogEnd(), CRGBA::Black);
545 /* Light case: CVisualCollisionManager use a fakeLight to avoid ShadowMapping on backFaces of meshs
546 Hence must clean all lights, and enable only the Light0 in driver
548 // Use CRenderTrav::resetLightSetup() to do so, to reset its Cache information
549 scene->getRenderTrav().resetLightSetup();
550 driver->enableLight(0, true);
553 // For each ShadowMap
554 for(uint i=0;i<_ShadowCasters.size();i++)
556 CTransform *caster= _ShadowCasters[i];
557 CShadowMap *sm= caster->getShadowMap();
558 nlassert(sm);
559 // NB: the ShadowCaster may not have a texture yet, for example because of Generate selection...
560 // If the final fade is 1, don't render!
561 if( sm->getTexture() && sm->getFinalFade()<1 )
563 CVector casterPos= caster->getWorldMatrix().getPos();
565 // Compute the World bbox (for quadGrid intersection)
566 CAABBox worldBB= sm->LocalBoundingBox;
567 worldBB.setCenter(worldBB.getCenter() + casterPos);
569 // compute the world matrix of the projection.
570 CMatrix worldProjMat= sm->LocalProjectionMatrix;
571 worldProjMat.setPos(worldProjMat.getPos()+casterPos);
573 // Now compute the textureMatrix, from WorldSpace to UV.
574 CMatrix wsTextMat;
575 wsTextMat= worldProjMat;
576 wsTextMat.invert();
578 // setup the Material.
579 _ReceiveShadowMaterial.setTexture(0, sm->getTexture());
580 /// Get The Mean Ambiant and Diffuse the caster receive (and cast, by approximation)
581 CRGBA ambient, diffuse;
582 computeShadowColors(scene, caster, ambient, diffuse);
583 // In some case, the ambient may be black, which cause problems because the shadow pop while diffuse fade.
584 // ThereFore, supose always a minimum of ambiant 10.
585 ambient.R= max(uint8(10), ambient.R);
586 ambient.G= max(uint8(10), ambient.G);
587 ambient.B= max(uint8(10), ambient.B);
588 // copute the shadowColor so that modulating a Medium diffuse terrain will get the correct result.
589 uint R= ambient.R + (diffuse.R>>1);
590 uint G= ambient.G + (diffuse.G>>1);
591 uint B= ambient.B + (diffuse.B>>1);
592 clamp(R, 1U, 256U);
593 clamp(G, 1U, 256U);
594 clamp(B, 1U, 256U);
595 /* screen= text*(a+d*0.5) (mean value). if we do shadowColor= a/(a+d*0.5f),
596 then we'll have "in theory" screen= text*a
598 R= (uint)(256 * ambient.R / (float)R);
599 G= (uint)(256 * ambient.G / (float)G);
600 B= (uint)(256 * ambient.B / (float)B);
601 clamp(R,0U,255U);
602 clamp(G,0U,255U);
603 clamp(B,0U,255U);
604 /// Finally "modulate" with FinalFade.
605 if(sm->getFinalFade()>0)
607 sint factor= OptFastFloor( 256 * sm->getFinalFade() );
608 clamp(factor, 0, 256);
609 R= 255*factor + R*(256-factor); R>>=8;
610 G= 255*factor + G*(256-factor); G>>=8;
611 B= 255*factor + B*(256-factor); B>>=8;
613 _ReceiveShadowMaterial.setColor(CRGBA(uint8(R),uint8(G),uint8(B),255));
615 // init the _ShadowMapProjector
616 _ShadowMapProjector.setWorldSpaceTextMat(wsTextMat);
618 // select receivers.
619 _ShadowReceiverGrid.select(worldBB.getMin(), worldBB.getMax());
620 // For all receivers
621 TShadowReceiverGrid::CIterator it;
622 for(it= _ShadowReceiverGrid.begin();it!=_ShadowReceiverGrid.end();it++)
624 CTransform *receiver= *it;
625 // Avoid Auto-Casting.
626 if(receiver==caster)
627 continue;
629 // update the material texture projection
630 // see getReceiverRenderWorldMatrix() Doc for why using this instead of getWorldMatrix()
631 _ShadowMapProjector.applyToMaterial(receiver->getReceiverRenderWorldMatrix(), _ReceiveShadowMaterial);
633 // cast the shadow on them
634 receiver->receiveShadowMap(sm, casterPos, _ReceiveShadowMaterial);
637 // Additionaly, the VisualCollisionManager may manage some shadow receiving
638 CVisualCollisionManager *shadowVcm= scene->getVisualCollisionManagerForShadow();
639 if(shadowVcm)
641 shadowVcm->receiveShadowMap(driver, sm, casterPos, _ReceiveShadowMaterial, _ShadowMapProjector);
646 // Restore fog color
647 driver->setupFog(driver->getFogStart(), driver->getFogEnd(), bkupFogColor);
649 // Leave Light Setup in a clean State
650 scene->getRenderTrav().resetLightSetup();
653 // TestYoyo. Display Projection BBox.
655 for(uint i=0;i<_ShadowCasters.size();i++)
657 // get the transform to compute shadow map.
658 CTransform *sc= _ShadowCasters[i];
660 CShadowMap *sm= sc->getShadowMap();
661 if(sm)
663 CVector p0= sm->LocalProjectionMatrix.getPos() + sc->getWorldMatrix().getPos();
664 IDriver &drv= *driver;
666 drv.setupModelMatrix(CMatrix::Identity);
668 CDRU::drawWiredBox(p0, sm->LocalProjectionMatrix.getI(), sm->LocalProjectionMatrix.getJ(),
669 sm->LocalProjectionMatrix.getK(), CRGBA::White, drv);
674 /* // hulud test
675 CScissor sic;
676 sic.initFullScreen();
677 // TODO_SHADOW: optim: need scissor?
678 driver->setupScissor(sic);
679 driver->setupViewMatrix(CMatrix::Identity);
680 driver->setupModelMatrix(CMatrix::Identity);
681 driver->setFrustum (0,1,0,1,-1,1,false);
682 // Render the shadow in the final shadow texture
683 CViewport vp;
684 vp.init (0, 0, 0.5f, 0.5f);
685 driver->setupViewport(vp);
687 static CVertexBuffer CopyQuads;
688 static CMaterial CopyMaterial;
689 CopyMaterial.initUnlit();
690 CopyMaterial.setColor(CRGBA::White);
691 CopyMaterial.setZWrite(false);
692 CopyMaterial.setZFunc(CMaterial::always);
693 CopyMaterial.setDoubleSided(true);
694 CopyMaterial.setBlend (false);
695 CopyMaterial.setAlphaTest (false);
696 CopyMaterial.setBlendFunc (CMaterial::one, CMaterial::zero);
697 CopyMaterial.texEnvOpRGB(0, CMaterial::Replace);
698 CopyMaterial.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcAlpha);
699 CopyQuads.setVertexFormat (CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag);
700 CopyQuads.setNumVertices(4);
702 CVertexBufferReadWrite vba;
703 CopyQuads.lock (vba);
704 vba.setVertexCoord (0, CVector (0, 0, 0));
705 vba.setVertexCoord (1, CVector (1, 0, 0));
706 vba.setVertexCoord (2, CVector (1, 0, 1));
707 vba.setVertexCoord (3, CVector (0, 0, 1));
708 vba.setTexCoord (0, 0, 0, 0);
709 vba.setTexCoord (1, 0, 1, 0);
710 vba.setTexCoord (2, 0, 1, 1);
711 vba.setTexCoord (3, 0, 0, 1);
713 driver->activeVertexBuffer (CopyQuads);
715 if (!_ShadowCasters.empty())
717 // get the transform to compute shadow map.
718 CTransform *sc= _ShadowCasters[0];
720 CShadowMap *sm= sc->getShadowMap();
721 if(sm)
723 CopyMaterial.setTexture (0, sm->getTexture());
724 driver->renderRawQuads (CopyMaterial, 0, 1);
729 // Release pass.
730 // ********
731 clearAllShadowCasters();
735 // ***************************************************************************
736 void CShadowMapManager::computeShadowDirection(CScene *scene, CTransform *sc, CVector &lightDir)
738 // merge the sunLight and pointLights into a single directional
739 lightDir= scene->getSunDirection();
740 const CLightContribution &lc= sc->getLightContribution();
741 // For Better result, weight with the light color too.
742 CRGBA color= scene->getSunDiffuse();
743 lightDir*= (float)lc.SunContribution * (color.R + color.G + color.B);
745 // merge pointLights
746 const CVector &modelPos= sc->getWorldMatrix().getPos();
747 for(uint i=0;i<NL3D_MAX_LIGHT_CONTRIBUTION;i++)
749 CPointLight *pl= lc.PointLight[i];
750 // End of List?
751 if(!pl)
752 break;
754 CVector plDir= modelPos - pl->getPosition();
755 plDir.normalize();
756 // Sum with this light, weighted by AttFactor, and light color
757 color= pl->getDiffuse();
758 lightDir+= plDir * (float)lc.AttFactor[i] * (float)(color.R + color.G + color.B);
761 // normalize merged dir
762 lightDir.normalize();
764 // clamp the light direction in z, according to Caster restriction
765 float zThre= sc->getShadowMapDirectionZThreshold();
766 if(lightDir.z>zThre)
768 /* normalize the x/y component so z=zthre
769 we want this: sqrt(x2+y2+z2)==1, which solve for x2+y2= 1-z2
770 the scale to apply to x and y is therefore deduced from:
771 sqr(scale)=(1-z2)/(x2+y2)
773 float scale= 0.f;
774 if(lightDir.x!=0.f || lightDir.y!=0.f)
775 scale= sqrtf( (1-sqr(zThre)) / (sqr(lightDir.x)+sqr(lightDir.y)) );
776 lightDir.x*= scale;
777 lightDir.y*= scale;
778 // force z component to be at least zthre
779 lightDir.z= zThre;
781 // re-normalize in case of precision problems
782 lightDir.normalize();
787 // ***************************************************************************
788 void CShadowMapManager::computeShadowColors(CScene *scene, CTransform *sc, CRGBA &ambient, CRGBA &diffuse)
790 const CLightContribution &lc= sc->getLightContribution();
792 // Get the current ambiant
793 ambient= lc.computeCurrentAmbient(scene->getSunAmbient());
795 // Compute the current diffuse as a sum (not a mean)
796 uint r, g, b;
797 CRGBA color= scene->getSunDiffuse();
798 r= color.R * lc.SunContribution;
799 g= color.G * lc.SunContribution;
800 b= color.B * lc.SunContribution;
802 // Add PointLights contribution
803 for(uint i=0;i<NL3D_MAX_LIGHT_CONTRIBUTION;i++)
805 CPointLight *pl= lc.PointLight[i];
806 // End of List?
807 if(!pl)
808 break;
810 // Sum with this light, weighted by AttFactor
811 color= pl->getDiffuse();
812 r+= color.R * lc.AttFactor[i];
813 g+= color.G * lc.AttFactor[i];
814 b+= color.B * lc.AttFactor[i];
817 // normalize
818 r>>=8;
819 g>>=8;
820 b>>=8;
822 // Don't take the MergedPointLight into consideration (should add to the diffuse part here, but rare case)
824 diffuse.R= uint8(min(r, 255U));
825 diffuse.G= uint8(min(g, 255U));
826 diffuse.B= uint8(min(b, 255U));
830 // ***************************************************************************
831 void CShadowMapManager::fillBlackBorder(IDriver *drv, uint numPassText, uint numTextW, uint numTextH, uint baseTextureSize)
833 if(numPassText==0)
834 return;
836 // the number of lines that have all their column disp.
837 uint numFullLine= numPassText/numTextW;
838 // for the last line not full, the number of column setuped
839 uint lastLineNumCol= numPassText - (numFullLine*numTextW);
840 // number of line including the last line if not empty
841 uint numTotalLine= numFullLine + (lastLineNumCol?1:0);
843 // Compute how many quads to render
844 uint numHQuads= numTotalLine * 2;
845 uint numTotalCol;
846 uint numVQuads;
847 if(numFullLine)
848 numTotalCol= numTextW;
849 else
850 numTotalCol= lastLineNumCol;
851 numVQuads= numTotalCol * 2;
853 _FillQuads.setNumVertices((numVQuads + numHQuads)*4);
855 // Fill HQuads.
856 uint i;
857 for(i=0;i<numTotalLine;i++)
859 uint w;
860 if(i<numFullLine)
861 w= numTextW*baseTextureSize;
862 else
863 w= lastLineNumCol*baseTextureSize;
864 // bottom of text
865 setBlackQuad(i*2+0, 0, i*baseTextureSize, w, 1);
866 // top of text
867 setBlackQuad(i*2+1, 0, (i+1)*baseTextureSize-1, w, 1);
870 // Fill VQuads;
871 uint baseId= numTotalLine*2;
872 for(i=0;i<numTotalCol;i++)
874 uint h;
875 if(i<lastLineNumCol)
876 h= numTotalLine*baseTextureSize;
877 else
878 h= numFullLine*baseTextureSize;
879 // left of text
880 setBlackQuad(baseId + i*2+0, i*baseTextureSize, 0, 1, h);
881 // right of text
882 setBlackQuad(baseId + i*2+1, (i+1)*baseTextureSize-1, 0, 1, h);
885 // Render Quads
886 _FillMaterial.setColor(CRGBA(0,0,0,0));
887 drv->activeVertexBuffer(_FillQuads);
888 drv->renderRawQuads(_FillMaterial, 0, numHQuads+numVQuads);
892 // ***************************************************************************
893 void CShadowMapManager::setBlackQuad(uint index, sint x, sint y, sint w, sint h)
895 float x0= (float)x;
896 float y0= (float)y;
897 float x1= (float)x+(float)w;
898 float y1= (float)y+(float)h;
899 index*= 4;
900 CVertexBufferReadWrite vba;
901 _FillQuads.lock(vba);
902 vba.setVertexCoord (index+0, CVector (x0, 0, y0));
903 vba.setVertexCoord (index+1, CVector (x1, 0, y0));
904 vba.setVertexCoord (index+2, CVector (x1, 0, y1));
905 vba.setVertexCoord (index+3, CVector (x0, 0, y1));
908 // ***************************************************************************
909 void CShadowMapManager::updateBlurTexture(IDriver &drv, uint w, uint h)
911 w= max(w, 2U);
912 h= max(h, 2U);
913 // if same size than setup, quit
914 if(_BlurTextureW==w && _BlurTextureH==h)
915 return;
917 // release old SmartPtr
918 uint i, j;
919 for (i=0; i<2; i++)
921 _BlurMaterial[i].setTexture(0, NULL);
922 _BlurMaterial[i].setTexture(1, NULL);
923 _BlurMaterial[i].setTexture(2, NULL);
924 _BlurMaterial[i].setTexture(3, NULL);
926 _BlurTexture[0]= NULL;
927 _BlurTexture[1]= NULL;
928 _BlurTextureW= w;
929 _BlurTextureH= h;
930 // NB: the format must be RGBA; else slow copyFrameBufferToTexture()
931 for (i=0; i<2; i++)
933 uint8 *tmpMem= new uint8[4*_BlurTextureW*_BlurTextureH];
934 _BlurTexture[i] = new CTextureMem (tmpMem, 4*_BlurTextureW*_BlurTextureH, true, false, _BlurTextureW, _BlurTextureH);
935 _BlurTexture[i]->setWrapS (ITexture::Clamp);
936 _BlurTexture[i]->setWrapT (ITexture::Clamp);
937 _BlurTexture[i]->setFilterMode (ITexture::Linear, ITexture::LinearMipMapOff);
938 _BlurTexture[i]->generate();
939 _BlurTexture[i]->setReleasable (false);
940 _BlurTexture[i]->setRenderTarget (true);
941 // For Texture Profiling
942 _BlurTexture[i]->setTextureCategory(_TextureCategory);
945 uint maxNumCstLighted;
946 uint maxNumCstUnlighted;
947 drv.getNumPerStageConstant(maxNumCstLighted, maxNumCstUnlighted);
949 // set to the material
950 for (i=0; i<2; i++)
952 for (j=0; j<maxNumCstUnlighted; j++)
954 _BlurMaterial[i].setTexture(j, _BlurTexture[i]);
958 // compute values for texturing
959 _BlurTextureOOW= 1.f / _BlurTextureW;
960 _BlurTextureOOH= 1.f / _BlurTextureH;
961 // The Delta HalfPixel
962 _BlurTextureD05W= 0.5f*_BlurTextureOOW;
963 _BlurTextureD05H= 0.5f*_BlurTextureOOH;
967 // ***************************************************************************
968 void CShadowMapManager::copyScreenToBlurTexture(IDriver *drv, uint numPassText, uint numTextW, uint numTextH, uint baseTextureSize)
970 if(numPassText==0)
971 return;
973 // TODO_SHADOW: optim: split into 2 copy for less pixel draw on the last line? No because of OverHead?
975 // number of line including the last line if not empty
976 // uint numTotalLine= (numPassText+numTextW-1)/numTextW;
977 // number of column.
978 // uint numTotalCol= (numPassText<numTextW)?numPassText:numTextW;
980 /* todo hulud shadows
981 drv->copyFrameBufferToTexture(_BlurTexture, 0, 0, 0, 0, 0, numTotalCol*baseTextureSize, numTotalLine*baseTextureSize); */
984 // ***************************************************************************
985 void CShadowMapManager::applyFakeGaussianBlur(IDriver *drv, uint numPassText, uint numTextW, uint numTextH, uint baseTextureSize, uint blurSource)
987 if(numPassText==0)
988 return;
990 // the number of lines that have all their column disp.
991 uint numFullLine= numPassText/numTextW;
992 // for the last line not full, the number of column setuped
993 uint lastLineNumCol= numPassText - (numFullLine*numTextW);
995 // Split into 2 quads. one for the first full lines, and one for the last not full line.
996 uint index= 0;
997 if(numFullLine)
998 setBlurQuadFakeGaussian(index++, 0, 0, numTextW*baseTextureSize, numFullLine*baseTextureSize);
999 if(lastLineNumCol)
1000 setBlurQuadFakeGaussian(index++, 0, numFullLine*baseTextureSize, lastLineNumCol*baseTextureSize, baseTextureSize);
1002 // render
1003 drv->activeVertexBuffer(_BlurQuads);
1004 drv->renderRawQuads(_BlurMaterial[blurSource], 0, index);
1008 // ***************************************************************************
1009 void CShadowMapManager::setBlurQuadFakeGaussian(uint index, sint x, sint y, sint w, sint h)
1011 float x0= (float)x;
1012 float y0= (float)y;
1013 float x1= (float)x+(float)w;
1014 float y1= (float)y+(float)h;
1015 float u0= x0*_BlurTextureOOW;
1016 float v0= y0*_BlurTextureOOH;
1017 float u1= x1*_BlurTextureOOW;
1018 float v1= y1*_BlurTextureOOH;
1019 index*= 4;
1021 // NB: the order of the Delta (--,++,-+,+-) is made so it works well with 2,3 or 4 texture support.
1023 // vertex 0
1024 CVertexBufferReadWrite vba;
1025 _BlurQuads.lock(vba);
1026 vba.setVertexCoord (index+0, CVector (x0, 0, y0));
1027 vba.setTexCoord(index+0, 0, u0-_BlurTextureD05W, v0-_BlurTextureD05H);
1028 vba.setTexCoord(index+0, 1, u0+_BlurTextureD05W, v0+_BlurTextureD05H);
1029 vba.setTexCoord(index+0, 2, u0-_BlurTextureD05W, v0+_BlurTextureD05H);
1030 vba.setTexCoord(index+0, 3, u0+_BlurTextureD05W, v0-_BlurTextureD05H);
1031 // vertex 1
1032 vba.setVertexCoord (index+1, CVector (x1, 0, y0));
1033 vba.setTexCoord(index+1, 0, u1-_BlurTextureD05W, v0-_BlurTextureD05H);
1034 vba.setTexCoord(index+1, 1, u1+_BlurTextureD05W, v0+_BlurTextureD05H);
1035 vba.setTexCoord(index+1, 2, u1-_BlurTextureD05W, v0+_BlurTextureD05H);
1036 vba.setTexCoord(index+1, 3, u1+_BlurTextureD05W, v0-_BlurTextureD05H);
1037 // vertex 2
1038 vba.setVertexCoord (index+2, CVector (x1, 0, y1));
1039 vba.setTexCoord(index+2, 0, u1-_BlurTextureD05W, v1-_BlurTextureD05H);
1040 vba.setTexCoord(index+2, 1, u1+_BlurTextureD05W, v1+_BlurTextureD05H);
1041 vba.setTexCoord(index+2, 2, u1-_BlurTextureD05W, v1+_BlurTextureD05H);
1042 vba.setTexCoord(index+2, 3, u1+_BlurTextureD05W, v1-_BlurTextureD05H);
1043 // vertex 3
1044 vba.setVertexCoord (index+3, CVector (x0, 0, y1));
1045 vba.setTexCoord(index+3, 0, u0-_BlurTextureD05W, v1-_BlurTextureD05H);
1046 vba.setTexCoord(index+3, 1, u0+_BlurTextureD05W, v1+_BlurTextureD05H);
1047 vba.setTexCoord(index+3, 2, u0-_BlurTextureD05W, v1+_BlurTextureD05H);
1048 vba.setTexCoord(index+3, 3, u0+_BlurTextureD05W, v1-_BlurTextureD05H);
1052 // ***************************************************************************
1053 struct CShadowMapSort
1055 CTransform *Caster;
1056 float Weight;
1058 bool operator<(const CShadowMapSort &o) const
1060 return Weight<o.Weight;
1064 // ***************************************************************************
1065 void CShadowMapManager::selectShadowMapsToGenerate(CScene *scene)
1067 // TODO: Scene option.
1068 const uint maxPerFrame= 8;
1069 const float minCamDist= 10;
1070 const CVector &camPos= scene->getRenderTrav().CamPos;
1071 uint i;
1073 // **** Clear first
1074 clearGenerateShadowCasters();
1076 // If the scene filter skeleton render, suppose no generation at all. Ugly.
1077 if(! (scene->getFilterRenderFlags() & UScene::FilterSkeleton) )
1078 return;
1080 // **** Select
1081 // For all ShadowCaster inserted
1082 static vector<CShadowMapSort> sortList;
1083 sortList.clear();
1084 sortList.reserve(_ShadowCasters.size());
1085 for(i=0;i<_ShadowCasters.size();i++)
1087 CTransform *caster= _ShadowCasters[i];
1088 /* If the shadowMap exist, and if not totaly faded
1089 NB: take FinalFade here because if 1, it won't be rendered in renderProject()
1090 so don't really need to update (useful for update reason, but LastGenerationFrame do the job)
1092 if(caster->getShadowMap() && caster->getShadowMap()->getFinalFade()<1 )
1094 CShadowMapSort sms;
1095 sms.Caster= caster;
1096 // The Weight is the positive delta of frame
1097 sms.Weight= (float)(scene->getNumRender() - caster->getShadowMap()->LastGenerationFrame);
1098 // Modulated by Caster Distance from Camera.
1099 float distToCam= (caster->getWorldMatrix().getPos() - camPos).norm();
1100 distToCam= max(distToCam, minCamDist);
1101 // The farthest, the less important
1102 sms.Weight/= distToCam;
1104 // Append
1105 sortList.push_back(sms);
1109 // Sort increasing
1110 sort(sortList.begin(), sortList.end());
1112 // Select the best
1113 uint numSel= min((uint)sortList.size(), maxPerFrame);
1114 _GenerateShadowCasters.resize(numSel);
1115 for(i= 0;i<numSel;i++)
1117 _GenerateShadowCasters[i]= sortList[sortList.size()-1-i].Caster;
1120 // **** Flag selecteds
1121 // For All selected models, indicate that they will generate shadowMap for this Frame.
1122 for(i=0;i<_GenerateShadowCasters.size();i++)
1124 _GenerateShadowCasters[i]->setGeneratingShadowMap(true);
1129 // ***************************************************************************
1130 void CShadowMapManager::clearAllShadowCasters()
1132 _ShadowReceiverGrid.clear();
1133 _ShadowCasters.clear();
1134 clearGenerateShadowCasters();
1138 // ***************************************************************************
1139 void CShadowMapManager::clearGenerateShadowCasters()
1141 // Reset first each flag of all models
1142 for(uint i=0;i<_GenerateShadowCasters.size();i++)
1144 _GenerateShadowCasters[i]->setGeneratingShadowMap(false);
1147 _GenerateShadowCasters.clear();
1150 // ***************************************************************************
1151 ITexture *CShadowMapManager::allocateTexture(uint textSize)
1153 nlassert( isPowerOf2(textSize) );
1155 // **** First, find a free texture already allocated.
1156 if(!_FreeShadowTextures.empty())
1158 ITexture *freeText= _FreeShadowTextures.back()->second;
1159 // If Ok for the size.
1160 if(freeText->getWidth() == textSize)
1162 // take this texture => no more free.
1163 _FreeShadowTextures.pop_back();
1164 return freeText;
1166 // else, suppose that we still take this slot.
1167 else
1169 // but since bad size, delete this slot from the map and create a new one (below)
1170 _ShadowTextureMap.erase(_FreeShadowTextures.back());
1171 // no more valid it
1172 _FreeShadowTextures.pop_back();
1176 // **** Else Allocate new one.
1177 // NB: the format must be RGBA; else slow copyFrameBufferToTexture()
1178 uint8 *tmpMem= new uint8[4*textSize*textSize];
1179 ITexture *text;
1180 text = new CTextureMem (tmpMem, 4*textSize*textSize, true, false, textSize, textSize);
1181 text->setWrapS (ITexture::Clamp);
1182 text->setWrapT (ITexture::Clamp);
1183 text->setFilterMode (ITexture::Linear, ITexture::LinearMipMapOff);
1184 text->generate();
1185 text->setReleasable (false);
1186 text->setRenderTarget (true);
1187 // For Texture Profiling
1188 text->setTextureCategory(_TextureCategory);
1190 // Setup in the map.
1191 _ShadowTextureMap[text]= text;
1193 return text;
1196 // ***************************************************************************
1197 void CShadowMapManager::releaseTexture(ITexture *text)
1199 if(!text)
1200 return;
1202 ItTextureMap it= _ShadowTextureMap.find(text);
1203 nlassert(it!=_ShadowTextureMap.end());
1205 // Don't release it, but insert in Free Space
1206 _FreeShadowTextures.push_back(it);
1209 // ***************************************************************************
1210 void CShadowMapManager::garbageShadowTextures(CScene *scene)
1212 uint defSize= scene->getShadowMapTextureSize();
1214 // For all Free Textures only, release the one that are no more of the wanted default ShadowMap Size.
1215 std::vector<ItTextureMap>::iterator itVec= _FreeShadowTextures.begin();
1216 for(;itVec!=_FreeShadowTextures.end();)
1218 if((*itVec)->second->getWidth() != defSize)
1220 // release the map texture iterator
1221 _ShadowTextureMap.erase(*itVec);
1222 // release the Vector Free iterator.
1223 itVec= _FreeShadowTextures.erase(itVec);
1225 else
1227 itVec++;
1231 // For memory optimisation, allow only a small extra of Texture allocated.
1232 if(_FreeShadowTextures.size()>NL3D_SMM_MAX_FREETEXT)
1234 // Release the extra texture (Hysteresis: divide by 2 the max wanted free to leave)
1235 uint startToFree= NL3D_SMM_MAX_FREETEXT/2;
1236 for(uint i=startToFree;i<_FreeShadowTextures.size();i++)
1238 // Free the texture entry.
1239 _ShadowTextureMap.erase(_FreeShadowTextures[i]);
1241 // resize vector
1242 _FreeShadowTextures.resize(startToFree);
1247 } // NL3D