shadow_map_manager.cpp

Go to the documentation of this file.
00001 
00005 /* Copyright, 2000-2003 Nevrax Ltd.
00006  *
00007  * This file is part of NEVRAX NEL.
00008  * NEVRAX NEL is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2, or (at your option)
00011  * any later version.
00012 
00013  * NEVRAX NEL is distributed in the hope that it will be useful, but
00014  * WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * General Public License for more details.
00017 
00018  * You should have received a copy of the GNU General Public License
00019  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00020  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00021  * MA 02111-1307, USA.
00022  */
00023 
00024 #include "std3d.h"
00025 #include "nel/3d/shadow_map_manager.h"
00026 #include "nel/misc/aabbox.h"
00027 #include "nel/3d/driver.h"
00028 #include "nel/3d/scene.h"
00029 #include "nel/3d/viewport.h"
00030 #include "nel/3d/scissor.h"
00031 #include "nel/3d/dru.h"
00032 #include "nel/3d/texture_mem.h"
00033 #include "nel/3d/visual_collision_manager.h"
00034 #include "nel/misc/hierarchical_timer.h"
00035 #include "nel/misc/fast_floor.h"
00036 
00037 
00038 using namespace NLMISC;
00039 using namespace std;
00040 
00041 namespace NL3D {
00042 
00043 
00044 // ***************************************************************************
00045 // easineasout
00046 static inline float easeInEaseOut(float x)
00047 {
00048     float   y;
00049     // cubic such that f(0)=0, f'(0)=0, f(1)=1, f'(1)=0.
00050     float   x2=x*x;
00051     float   x3=x2*x;
00052     y= -2*x3 + 3*x2;
00053     return y;
00054 }
00055 
00056 
00057 // ***************************************************************************
00058 CShadowMapManager::CShadowMapManager()
00059 {
00060     uint    i;
00061 
00062     // For Texture profiling
00063     _TextureCategory= new ITexture::CTextureCategory("SHADOW MANAGER");
00064 
00065     setQuadGridSize(NL3D_SMM_QUADGRID_SIZE, NL3D_SMM_QUADCELL_SIZE);
00066     _ShadowCasters.reserve(256);
00067     _GenerateShadowCasters.reserve(256);
00068     _PolySmooth= true;
00069 
00070     // **** Setup Fill
00071     _FillQuads.setVertexFormat(CVertexBuffer::PositionFlag);
00072     _FillMaterial.initUnlit();
00073     _FillMaterial.setColor(CRGBA(0,0,0,0));
00074     _FillMaterial.setZWrite(false);
00075     _FillMaterial.setZFunc(CMaterial::always);
00076     _FillMaterial.setDoubleSided(true);
00077     _FillQuads.setPreferredMemory(CVertexBuffer::RAMVolatile, true);
00078 
00079     // **** Setup Blur
00080     _BlurQuads.setVertexFormat(CVertexBuffer::PositionFlag |
00081         CVertexBuffer::TexCoord0Flag |
00082         CVertexBuffer::TexCoord1Flag |
00083         CVertexBuffer::TexCoord2Flag |
00084         CVertexBuffer::TexCoord3Flag);
00085     _BlurQuads.setPreferredMemory(CVertexBuffer::RAMVolatile, true);
00086 
00087     // Only 2 quads are used to blur
00088     _BlurQuads.setNumVertices(8);
00089     for (i=0;i<2;i++)
00090     {
00091         _BlurMaterial[i].initUnlit();
00092         _BlurMaterial[i].setColor(CRGBA::White);
00093         _BlurMaterial[i].setZWrite(false);
00094         _BlurMaterial[i].setZFunc(CMaterial::always);
00095         _BlurMaterial[i].setDoubleSided(true);
00096         // Setup The Blur. NB: it will take advantage of Max 4 texture driver support, but will still
00097         // work with 2 or 3 (less beautifull).
00098         uint j;
00099         for(j=1;j<4;j++)
00100         {
00101             _BlurMaterial[i].texEnvOpRGB(j, CMaterial::InterpolateConstant);
00102             _BlurMaterial[i].texEnvArg0RGB(j, CMaterial::Texture, CMaterial::SrcColor);
00103             _BlurMaterial[i].texEnvArg1RGB(j, CMaterial::Previous, CMaterial::SrcColor);
00104             _BlurMaterial[i].texEnvOpAlpha(j, CMaterial::InterpolateConstant);
00105             _BlurMaterial[i].texEnvArg0Alpha(j, CMaterial::Texture, CMaterial::SrcAlpha);
00106             _BlurMaterial[i].texEnvArg1Alpha(j, CMaterial::Previous, CMaterial::SrcAlpha);
00107         }
00108         // Factor for Stage so the sum is 1.
00109         _BlurMaterial[i].texConstantColor(1, CRGBA(128,128,128,128));       // factor= 1/2
00110         _BlurMaterial[i].texConstantColor(2, CRGBA(85,85,85,85));           // factor= 1/3
00111         _BlurMaterial[i].texConstantColor(3, CRGBA(64,64,64,64));           // factor= 1/4
00112     }
00113 
00114     _BlurTextureW= 0;
00115     _BlurTextureH= 0;
00116 
00117     // *** Setup copy
00118     _CopyQuads.setVertexFormat (CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag);
00119     _CopyQuads.setNumVertices(4);
00120     _CopyQuads.setPreferredMemory(CVertexBuffer::RAMVolatile, true);
00121     CVertexBufferReadWrite vba;
00122     _CopyQuads.lock (vba);
00123     vba.setVertexCoord (0, CVector (0, 0, 0));
00124     vba.setVertexCoord (1, CVector (1, 0, 0));
00125     vba.setVertexCoord (2, CVector (1, 0, 1));
00126     vba.setVertexCoord (3, CVector (0, 0, 1));
00127 
00128     // Copy material
00129     _CopyMaterial.initUnlit();
00130     _CopyMaterial.setColor(CRGBA::White);
00131     _CopyMaterial.setZWrite(false);
00132     _CopyMaterial.setZFunc(CMaterial::always);
00133     _CopyMaterial.setDoubleSided(true);
00134     _CopyMaterial.setBlend (false);
00135     _CopyMaterial.setAlphaTest (false);
00136     _CopyMaterial.setBlendFunc (CMaterial::one, CMaterial::zero);
00137 
00138     // **** Setup Receiving
00139 
00140     // Setup the clamp texture.
00141     const   uint    clampTextSize= 512;
00142     const   uint    clampNearFadeSize= 32;
00143     const   uint    clampFarFadeSize= 128;
00144     uint            textMemSize= 4*clampTextSize*1;
00145     // Fill mem
00146     uint8   *tmpMem= new uint8[textMemSize];
00147     memset(tmpMem, 255, textMemSize);
00148     for(i=0;i<clampNearFadeSize;++i)
00149     {
00150         float   f= (float)i/clampNearFadeSize;
00151         f= easeInEaseOut(f);
00152         tmpMem[4*i+3]= uint8(255.f*f);
00153     }
00154     for(i=0;i<clampFarFadeSize;++i)
00155     {
00156         float   f= (float)i/clampFarFadeSize;
00157         f= easeInEaseOut(f);
00158         tmpMem[4*(clampTextSize-i-1)+3]= uint8(255.f*f);
00159     }
00160     // build the texture
00161     _ClampTexture = new CTextureMem (tmpMem, 4*clampTextSize*1, true, false, clampTextSize, 1);
00162     _ClampTexture->setWrapS (ITexture::Clamp);
00163     _ClampTexture->setWrapT (ITexture::Clamp);
00164     _ClampTexture->setFilterMode (ITexture::Linear, ITexture::LinearMipMapOff);
00165     _ClampTexture->generate();
00166     _ClampTexture->setReleasable (false);
00167     // For Texture Profiling
00168     _ClampTexture->setTextureCategory(_TextureCategory);
00169 
00170     // init material
00171     _ReceiveShadowMaterial.initUnlit();
00172     _ReceiveShadowMaterial.setBlend(true);
00173     _ReceiveShadowMaterial.setBlendFunc(CMaterial::zero, CMaterial::invsrccolor);
00174     _ReceiveShadowMaterial.setZWrite(false);
00175     // FillRate Optim
00176     _ReceiveShadowMaterial.setAlphaTest(true);
00177     _ReceiveShadowMaterial.setAlphaTestThreshold(0.01f);
00178 
00179     // ---- Stage 0. Project the ShadowMap. Blend the color between ShadowColor and White.
00180     // setup texture coord gen
00181     _ReceiveShadowMaterial.enableUserTexMat(0, true);
00182     _ReceiveShadowMaterial.setTexCoordGen(0, true);
00183     _ReceiveShadowMaterial.setTexCoordGenMode(0, CMaterial::TexCoordGenObjectSpace);
00184     // Setup the stage so we interpolate ShadowColor and White (according to shadowmap alpha)
00185     // 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
00186     // source alpha & inverse diffuse.  then invert result at subsequent stage
00187     _ReceiveShadowMaterial.texEnvOpRGB(0, CMaterial::Modulate);
00188     _ReceiveShadowMaterial.texEnvArg0RGB(0, CMaterial::Diffuse, CMaterial::InvSrcColor);
00189     _ReceiveShadowMaterial.texEnvArg1RGB(0, CMaterial::Texture, CMaterial::SrcAlpha);
00190     // Take Alpha for AlphaTest only.
00191     _ReceiveShadowMaterial.texEnvOpAlpha(0, CMaterial::Replace);
00192     _ReceiveShadowMaterial.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);
00193 
00194     // ---- Stage 1. "Modulate" by Clamp Texture. Blend the color between stage0 color and White.
00195     // setup texture coord gen
00196     _ReceiveShadowMaterial.enableUserTexMat(1, true);
00197     _ReceiveShadowMaterial.setTexCoordGen(1, true);
00198     _ReceiveShadowMaterial.setTexCoordGenMode(1, CMaterial::TexCoordGenObjectSpace);
00199     _ReceiveShadowMaterial.setTexture(1, _ClampTexture);
00200     // Setup the stage so we interpolate Shadow and White (according to clamp alpha)
00201     _ReceiveShadowMaterial.texEnvOpRGB(1, CMaterial::Modulate);
00202     _ReceiveShadowMaterial.texEnvArg0RGB(1, CMaterial::Previous, CMaterial::SrcColor); // Color is inverted before the blend
00203     _ReceiveShadowMaterial.texEnvArg1RGB(1, CMaterial::Texture, CMaterial::SrcAlpha);
00204     // Take Alpha for AlphaTest only. (take 1st texture alpha...)
00205     _ReceiveShadowMaterial.texEnvOpAlpha(0, CMaterial::Replace);
00206     _ReceiveShadowMaterial.texEnvArg0Alpha(0, CMaterial::Previous, CMaterial::SrcAlpha);
00207 
00208     // **** Setup Casting
00209     _CasterShadowMaterial.initUnlit();
00210     _CasterShadowMaterial.setColor(CRGBA::White);
00211     _CasterShadowMaterial.setZWrite(false);
00212     _CasterShadowMaterial.setZFunc(CMaterial::always);
00213     _CasterShadowMaterial.setDoubleSided(true);
00214     // Alpha Polygon coverage accumulate, for polygon smoothing
00215     _CasterShadowMaterial.setBlend(true);
00216     _CasterShadowMaterial.setBlendFunc(CMaterial::one, CMaterial::one);
00217 
00218     _BlurQuads.setName("CShadowMapManager::_BlurQuads");
00219     _FillQuads.setName("CShadowMapManager::_FillQuads");
00220     _CopyQuads.setName("CShadowMapManager::_CopyQuads");
00221 }
00222 
00223 // ***************************************************************************
00224 CShadowMapManager::~CShadowMapManager()
00225 {
00226     clearAllShadowCasters();
00227 }
00228 
00229 // ***************************************************************************
00230 void            CShadowMapManager::setQuadGridSize(uint size, float cellSize)
00231 {
00232     _ShadowReceiverGrid.create(size, cellSize);
00233 }
00234 
00235 // ***************************************************************************
00236 void            CShadowMapManager::addShadowCaster(CTransform *model)
00237 {
00238     _ShadowCasters.push_back(model);
00239 }
00240 
00241 // ***************************************************************************
00242 void            CShadowMapManager::addShadowReceiver(CTransform *model)
00243 {
00244     CAABBox bb;
00245     model->getReceiverBBox(bb);
00246 
00247     _ShadowReceiverGrid.insert(bb.getMin(), bb.getMax(), model);
00248 }
00249 
00250 // ***************************************************************************
00251 void            CShadowMapManager::renderGenerate(CScene *scene)
00252 {
00253     H_AUTO( NL3D_ShadowManager_Generate );
00254 
00255     // Each frame, do a small garbage collector for unused free textures.
00256     garbageShadowTextures(scene);
00257 
00258     IDriver *driverForShadowGeneration= scene->getRenderTrav().getAuxDriver();
00259 
00260     // Init
00261     // ********
00262     uint32  wndW= _BlurTextureW, wndH= _BlurTextureH;
00263     // get some text/screen size.
00264     if(driverForShadowGeneration)
00265         driverForShadowGeneration->getWindowSize(wndW, wndH);
00266     uint    baseTextureSize= scene->getShadowMapTextureSize();
00267     // Minimize the Dest Texture size, so the blurTexture don't get to heavy in VRAM.
00268     uint32  textDestW= min(wndW, (uint32)NL3D_SMM_MAX_TEXTDEST_SIZE);
00269     uint32  textDestH= min(wndH, (uint32)NL3D_SMM_MAX_TEXTDEST_SIZE);
00270 
00271     // if not needed or if not possible, exit. test for wndSize is also important when window is minimized
00272     if( _ShadowCasters.empty() ||
00273         textDestW<baseTextureSize || textDestH<baseTextureSize)
00274     {
00275         clearAllShadowCasters();
00276         return;
00277     }
00278 
00279     // If Needed to project some ShadowCaster, but none to compute this frame, quit.
00280     if( _GenerateShadowCasters.empty() )
00281     {
00282         // But here don't reset since the renderProject() will do job
00283         return;
00284     }
00285 
00286     // get the number of shadowMap compute we can do in one screen.
00287     uint    numTextW= textDestW/baseTextureSize;
00288     uint    numTextH= textDestH/baseTextureSize;
00289     if(!isPowerOf2(numTextW))
00290         numTextW= raiseToNextPowerOf2(numTextW)/2;
00291     if(!isPowerOf2(numTextH))
00292         numTextH= raiseToNextPowerOf2(numTextH)/2;
00293     // the max shadow casters we can do in 1 screen pass.
00294     uint    maxSCPerPass= numTextW * numTextH;
00295 
00296     // compute vp float size.
00297     float   vpWidth= (float)baseTextureSize / (float)(numTextW*baseTextureSize);
00298     float   vpHeight= (float)baseTextureSize / (float)(numTextH*baseTextureSize);
00299 
00300 
00301     // Create / Update the Blur Texture
00302     updateBlurTexture(*driverForShadowGeneration, numTextW * baseTextureSize, numTextH * baseTextureSize);
00303 
00304 
00305     // Do NPass if a screen is not sufficient to render all shadow maps...
00306     // ********
00307 
00308     // bkup driver state
00309     CViewport   bkupViewport;
00310     driverForShadowGeneration->getViewport(bkupViewport);
00311     bool        bkupFog= driverForShadowGeneration->fogEnabled();
00312 
00313     // setup some state
00314     driverForShadowGeneration->enableFog(false);
00315     // Allow Writing on alpha only. => don't write on RGB objects!
00316     driverForShadowGeneration->setColorMask(false, false, false, true);
00317 
00318     uint    numSC= _GenerateShadowCasters.size();
00319     uint    baseSC= 0;
00320     while(numSC>0)
00321     {
00322         uint    numPassSC= min(maxSCPerPass, numSC);
00323         // number of line including the last line if not empty
00324         uint    numTotalLine= (numPassSC+numTextW-1)/numTextW;
00325         // number of column.
00326         uint    numTotalCol= (numPassSC<numTextW)?numPassSC:numTextW;
00327 
00328         // Render to the blur texture
00329         driverForShadowGeneration->setRenderTarget (_BlurTexture[0], 0, 0, numTotalCol*baseTextureSize, numTotalLine*baseTextureSize);
00330 
00331         uint    textX, textY;
00332         uint    i;
00333 
00334         // Render All Shadow Map
00335         // ********
00336 
00337         // Render the polygons with Smooth Anti-Alias. Less jittering for little performance overcost
00338         if(_PolySmooth)
00339             driverForShadowGeneration->enablePolygonSmoothing(true);
00340 
00341         textX=0;
00342         textY=0;
00343         for(i=0;i<numPassSC;i++)
00344         {
00345             // get the transform to compute shadow map.
00346             CTransform  *sc= _GenerateShadowCasters[baseSC+i];
00347 
00348             // select the shadow direction
00349             CVector     lightDir;
00350             computeShadowDirection(scene, sc, lightDir);
00351 
00352             // setup viewport to render to
00353             CViewport   vp;
00354             vp.init(textX*baseTextureSize/(float)_BlurTextureW, textY*baseTextureSize/(float)_BlurTextureH, vpWidth, vpHeight);
00355             driverForShadowGeneration->setupViewport(vp);
00356 
00357             // TODO_SHADOW: optim: one big erase per pass, but just bbox needed (according to number of SC to render)
00358             // do a siccor or prefer do a polygon clear?
00359             CScissor    sic;
00360             sic.init(textX*baseTextureSize/(float)_BlurTextureW, textY*baseTextureSize/(float)_BlurTextureH, vpWidth, vpHeight);
00361             driverForShadowGeneration->setupScissor(sic);
00362             driverForShadowGeneration->clear2D(CRGBA(0,0,0,0));
00363 
00364             // render to screen
00365             sc->generateShadowMap(lightDir);
00366 
00367             // next text
00368             textX++;
00369             if(textX==numTextW)
00370             {
00371                 textX= 0;
00372                 textY++;
00373             }
00374         }
00375 
00376         // Restore
00377         if(_PolySmooth)
00378             driverForShadowGeneration->enablePolygonSmoothing(false);
00379 
00380         // For Subsequent operations, setup a full viewport and a "Screen Frustum"
00381         CScissor    sic;
00382         sic.initFullScreen();
00383         // TODO_SHADOW: optim: need scissor?
00384         driverForShadowGeneration->setupScissor(sic);
00385         CViewport   vp;
00386         vp.initFullScreen();
00387         driverForShadowGeneration->setupViewport(vp);
00388         driverForShadowGeneration->setFrustum(0, (float)_BlurTextureW, 0, (float)_BlurTextureH, -1,1,false);
00389         driverForShadowGeneration->setupViewMatrix(CMatrix::Identity);
00390         driverForShadowGeneration->setupModelMatrix(CMatrix::Identity);
00391 
00392         // Ensure the One pixel black security on texture border
00393         fillBlackBorder(driverForShadowGeneration, numPassSC, numTextW, numTextH, baseTextureSize);
00394 
00395         // Blur.
00396         // ********
00397         uint    numBlur= scene->getShadowMapBlurSize();
00398         clamp(numBlur, 0U, 3U);
00399         uint blurTarget = 0;
00400         for(i=0;i<numBlur;i++)
00401         {
00402             // Set the blur texture target
00403             blurTarget = (i+1)&1;
00404             const uint blurSource = i&1;
00405             driverForShadowGeneration->setRenderTarget (_BlurTexture[blurTarget], 0, 0, numTotalCol*baseTextureSize, numTotalLine*baseTextureSize);
00406 
00407             // blur
00408             applyFakeGaussianBlur(driverForShadowGeneration, numPassSC, numTextW, numTextH, baseTextureSize, blurSource);
00409 
00410             // Ensure the One pixel black security on texture border
00411             fillBlackBorder(driverForShadowGeneration, numPassSC, numTextW, numTextH, baseTextureSize);
00412         }
00413 
00414         // Copy the last blur texture
00415         _CopyMaterial.setTexture(0, _BlurTexture[blurTarget]);
00416 
00417         // Store Screen in ShadowMaps
00418         // ********
00419         textX=0;
00420         textY=0;
00421         for(i=0;i<numPassSC;i++)
00422         {
00423             // get the transform to compute shadow map.
00424             CTransform  *sc= _GenerateShadowCasters[baseSC+i];
00425             CShadowMap  *sm= sc->getShadowMap();
00426             if(sm)
00427             {
00428                 ITexture    *text= sm->getTexture();
00429                 if(text)
00430                 {
00431                     uint    bts= baseTextureSize;
00432 
00433                     // todo hulud : Try the temporary buffer trick (openGL)
00434                     //if (!driverForShadowGeneration->copyTargetToTexture (text, 0, 0, textX*bts, textY*bts, bts, bts))
00435                     {
00436                         // Copy the texture
00437                         driverForShadowGeneration->setRenderTarget (text, 0, 0, bts, bts);
00438                         driverForShadowGeneration->clear2D (CRGBA(0,0,0,0));
00439 
00440                         // Viewport is already fullscreen, set the frustum
00441                         driverForShadowGeneration->setFrustum(0, 1, 0, 1, -1,1,false);
00442 
00443                         // Set the vertex buffer UV
00444                         {
00445                             CVertexBufferReadWrite vba;
00446                             _CopyQuads.lock (vba);
00447                             const float u= (float)(textX*bts)*_BlurTextureOOW;
00448                             const float v= (float)(textY*bts)*_BlurTextureOOH;
00449                             const float width= (float)bts*_BlurTextureOOW;
00450                             const float height= (float)bts*_BlurTextureOOH;
00451                             vba.setTexCoord (0, 0, u, v);
00452                             vba.setTexCoord (1, 0, u+width, v);
00453                             vba.setTexCoord (2, 0, u+width, v+height);
00454                             vba.setTexCoord (3, 0, u, v+height);
00455                         }
00456 
00457                         // Vertex buffer
00458                         driverForShadowGeneration->activeVertexBuffer (_CopyQuads);
00459 
00460                         CScissor    sic;
00461                         sic.initFullScreen();
00462                         // TODO_SHADOW: optim: need scissor?
00463                         driverForShadowGeneration->setupScissor(sic);
00464 
00465                         driverForShadowGeneration->setupViewMatrix(CMatrix::Identity);
00466                         driverForShadowGeneration->setupModelMatrix(CMatrix::Identity);
00467 
00468                         // Render the shadow in the final shadow texture
00469                         vp.init (0, 0, 1, 1);
00470                         driverForShadowGeneration->setupViewport(vp);
00471                         driverForShadowGeneration->renderRawQuads (_CopyMaterial, 0, 1);
00472 
00473                         // Set default render target
00474                         driverForShadowGeneration->setRenderTarget (NULL);
00475                     }
00476 
00477                     // Indicate to the ShadowMap that we have updated his Texture
00478                     sm->LastGenerationFrame= scene->getNumRender();
00479                 }
00480             }
00481 
00482             // next text
00483             textX++;
00484             if(textX==numTextW)
00485             {
00486                 textX= 0;
00487                 textY++;
00488             }
00489         }
00490 
00491 
00492         // next screen pass.
00493         baseSC+= numPassSC;
00494         numSC-= numPassSC;
00495     }
00496 
00497     // Set default render target
00498     driverForShadowGeneration->setRenderTarget (NULL);
00499 
00500     // Allow Writing on all.
00501     driverForShadowGeneration->setColorMask(true, true, true, true);
00502     // Restore driver state. (do it here because driverForShadowGeneration may be the main screen).
00503     driverForShadowGeneration->setupViewport(bkupViewport);
00504     driverForShadowGeneration->enableFog(bkupFog);
00505     // TODO_SHADOW: optim need scissor?
00506     CScissor    sic;
00507     sic.initFullScreen();
00508     driverForShadowGeneration->setupScissor(sic);
00509 
00510     // ensure the Scene Driver has correct matrix setup (in common case where AuxDriver == Std Driver)
00511     scene->getRenderTrav().setupDriverCamera();
00512 
00513 
00514     // Clear ShadowCaster Generation
00515     clearGenerateShadowCasters();
00516 }
00517 
00518 // ***************************************************************************
00519 void            CShadowMapManager::renderProject(CScene *scene)
00520 {
00521     // if not needed exit. NB renderGenerate() must have been called before.
00522     if( _ShadowCasters.empty() )
00523     {
00524         return;
00525     }
00526 
00527 
00528     // Project ShadowMap on receivers.
00529     // ********
00530 
00531     H_AUTO( NL3D_ShadowManager_Project );
00532 
00533 
00534     /* Fog Case: Since we do a modulate, we don't want to modulate the fog color with himself.
00535         Instead, if the shadowed pixel is in full fog, we have to modulate him with Blac (modulate with INVERSE-source color, actually ...)
00536         => replace fog color with black temporarily
00537     */
00538     IDriver *driver= scene->getRenderTrav().getDriver();
00539     CRGBA   bkupFogColor= driver->getFogColor();
00540 
00541 
00542     driver->setupFog(driver->getFogStart(), driver->getFogEnd(), CRGBA::Black);
00543 
00544     /* Light case: CVisualCollisionManager use a fakeLight to avoid ShadowMapping on backFaces of meshs
00545         Hence must clean all lights, and enalbe only the Light0 in driver
00546     */
00547     // Use CRenderTrav::resetLightSetup() to do so, to reset its Cache information
00548     scene->getRenderTrav().resetLightSetup();
00549     driver->enableLight(0, true);
00550 
00551 
00552     // For each ShadowMap
00553     for(uint i=0;i<_ShadowCasters.size();i++)
00554     {
00555         CTransform  *caster= _ShadowCasters[i];
00556         CShadowMap  *sm= caster->getShadowMap();
00557         nlassert(sm);
00558         // NB: the ShadowCaster may not have a texture yet, for example because of Generate selection...
00559         // If the final fade is 1, don't render!
00560         if( sm->getTexture() && sm->getFinalFade()<1 )
00561         {
00562             CVector     casterPos= caster->getWorldMatrix().getPos();
00563 
00564             // Compute the World bbox (for quadGrid intersection)
00565             CAABBox     worldBB= sm->LocalBoundingBox;
00566             worldBB.setCenter(worldBB.getCenter() + casterPos);
00567 
00568             // compute the world matrix of the projection.
00569             CMatrix     worldProjMat= sm->LocalProjectionMatrix;
00570             worldProjMat.setPos(worldProjMat.getPos()+casterPos);
00571 
00572             // Now compute the textureMatrix, from WorldSpace to UV.
00573             CMatrix     wsTextMat;
00574             wsTextMat= worldProjMat;
00575             wsTextMat.invert();
00576 
00577             // setup the Material.
00578             _ReceiveShadowMaterial.setTexture(0, sm->getTexture());
00580             CRGBA   ambient, diffuse;
00581             computeShadowColors(scene, caster, ambient, diffuse);
00582             // In some case, the ambient may be black, which cause problems because the shadow pop while diffuse fade.
00583             // ThereFore, supose always a minimum of ambiant 10.
00584             ambient.R= max(uint8(10), ambient.R);
00585             ambient.G= max(uint8(10), ambient.G);
00586             ambient.B= max(uint8(10), ambient.B);
00587             // copute the shadowColor so that modulating a Medium diffuse terrain will  get the correct result.
00588             uint    R= ambient.R + (diffuse.R>>1);
00589             uint    G= ambient.G + (diffuse.G>>1);
00590             uint    B= ambient.B + (diffuse.B>>1);
00591             clamp(R, 1U, 256U);
00592             clamp(G, 1U, 256U);
00593             clamp(B, 1U, 256U);
00594             /* screen= text*(a+d*0.5) (mean value). if we do shadowColor= a/(a+d*0.5f),
00595                 then we'll have "in theory"  screen= text*a
00596             */
00597             R= (uint)(256 * ambient.R / (float)R);
00598             G= (uint)(256 * ambient.G / (float)G);
00599             B= (uint)(256 * ambient.B / (float)B);
00600             clamp(R,0U,255U);
00601             clamp(G,0U,255U);
00602             clamp(B,0U,255U);
00604             if(sm->getFinalFade()>0)
00605             {
00606                 sint    factor= OptFastFloor( 256 * sm->getFinalFade() );
00607                 clamp(factor, 0, 256);
00608                 R= 255*factor + R*(256-factor); R>>=8;
00609                 G= 255*factor + G*(256-factor); G>>=8;
00610                 B= 255*factor + B*(256-factor); B>>=8;
00611             }
00612             _ReceiveShadowMaterial.setColor(CRGBA(uint8(R),uint8(G),uint8(B),255));
00613 
00614             // init the _ShadowMapProjector
00615             _ShadowMapProjector.setWorldSpaceTextMat(wsTextMat);
00616 
00617             // select receivers.
00618             _ShadowReceiverGrid.select(worldBB.getMin(), worldBB.getMax());
00619             // For all receivers
00620             TShadowReceiverGrid::CIterator  it;
00621             for(it= _ShadowReceiverGrid.begin();it!=_ShadowReceiverGrid.end();it++)
00622             {
00623                 CTransform  *receiver= *it;
00624                 // Avoid Auto-Casting.
00625                 if(receiver==caster)
00626                     continue;
00627 
00628                 // update the material texture projection
00629                 // see getReceiverRenderWorldMatrix() Doc for why using this instead of getWorldMatrix()
00630                 _ShadowMapProjector.applyToMaterial(receiver->getReceiverRenderWorldMatrix(), _ReceiveShadowMaterial);
00631 
00632                 // cast the shadow on them
00633                 receiver->receiveShadowMap(sm, casterPos, _ReceiveShadowMaterial);
00634             }
00635 
00636             // Additionaly, the VisualCollisionManager may manage some shadow receiving
00637             CVisualCollisionManager     *shadowVcm= scene->getVisualCollisionManagerForShadow();
00638             if(shadowVcm)
00639             {
00640                 shadowVcm->receiveShadowMap(driver, sm, casterPos, _ReceiveShadowMaterial, _ShadowMapProjector);
00641             }
00642         }
00643     }
00644 
00645     // Restore fog color
00646     driver->setupFog(driver->getFogStart(), driver->getFogEnd(), bkupFogColor);
00647 
00648     // Leave Light Setup in a clean State
00649     scene->getRenderTrav().resetLightSetup();
00650 
00651 
00652     // TestYoyo. Display Projection BBox.
00653     /*{
00654         for(uint i=0;i<_ShadowCasters.size();i++)
00655         {
00656             // get the transform to compute shadow map.
00657             CTransform  *sc= _ShadowCasters[i];
00658 
00659             CShadowMap  *sm= sc->getShadowMap();
00660             if(sm)
00661             {
00662                 CVector     p0= sm->LocalProjectionMatrix.getPos() + sc->getWorldMatrix().getPos();
00663                 IDriver     &drv= *driver;
00664 
00665                 drv.setupModelMatrix(CMatrix::Identity);
00666 
00667                 CDRU::drawWiredBox(p0, sm->LocalProjectionMatrix.getI(), sm->LocalProjectionMatrix.getJ(),
00668                     sm->LocalProjectionMatrix.getK(), CRGBA::White, drv);
00669             }
00670         }
00671     }*/
00672 
00673     /* // hulud test
00674     CScissor    sic;
00675     sic.initFullScreen();
00676     // TODO_SHADOW: optim: need scissor?
00677     driver->setupScissor(sic);
00678     driver->setupViewMatrix(CMatrix::Identity);
00679     driver->setupModelMatrix(CMatrix::Identity);
00680     driver->setFrustum (0,1,0,1,-1,1,false);
00681     // Render the shadow in the final shadow texture
00682     CViewport vp;
00683     vp.init (0, 0, 0.5f, 0.5f);
00684     driver->setupViewport(vp);
00685 
00686     static CVertexBuffer CopyQuads;
00687     static CMaterial CopyMaterial;
00688     CopyMaterial.initUnlit();
00689     CopyMaterial.setColor(CRGBA::White);
00690     CopyMaterial.setZWrite(false);
00691     CopyMaterial.setZFunc(CMaterial::always);
00692     CopyMaterial.setDoubleSided(true);
00693     CopyMaterial.setBlend (false);
00694     CopyMaterial.setAlphaTest (false);
00695     CopyMaterial.setBlendFunc (CMaterial::one, CMaterial::zero);
00696     CopyMaterial.texEnvOpRGB(0, CMaterial::Replace);
00697     CopyMaterial.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcAlpha);
00698     CopyQuads.setVertexFormat (CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag);
00699     CopyQuads.setNumVertices(4);
00700     {
00701         CVertexBufferReadWrite vba;
00702         CopyQuads.lock (vba);
00703         vba.setVertexCoord (0, CVector (0, 0, 0));
00704         vba.setVertexCoord (1, CVector (1, 0, 0));
00705         vba.setVertexCoord (2, CVector (1, 0, 1));
00706         vba.setVertexCoord (3, CVector (0, 0, 1));
00707         vba.setTexCoord (0, 0, 0, 0);
00708         vba.setTexCoord (1, 0, 1, 0);
00709         vba.setTexCoord (2, 0, 1, 1);
00710         vba.setTexCoord (3, 0, 0, 1);
00711     }
00712     driver->activeVertexBuffer (CopyQuads);
00713 
00714     if (_ShadowCasters.size()>0)
00715     {
00716         // get the transform to compute shadow map.
00717         CTransform  *sc= _ShadowCasters[0];
00718 
00719         CShadowMap  *sm= sc->getShadowMap();
00720         if(sm)
00721         {
00722             CopyMaterial.setTexture (0, sm->getTexture());
00723             driver->renderRawQuads (CopyMaterial, 0, 1);
00724         }
00725     }*/
00726 
00727 
00728     // Release pass.
00729     // ********
00730     clearAllShadowCasters();
00731 }
00732 
00733 
00734 // ***************************************************************************
00735 void            CShadowMapManager::computeShadowDirection(CScene *scene, CTransform *sc, CVector &lightDir)
00736 {
00737     // merge the sunLight and pointLights into a single directional
00738     lightDir= scene->getSunDirection();
00739     const   CLightContribution  &lc= sc->getLightContribution();
00740     // For Better result, weight with the light color too.
00741     CRGBA   color= scene->getSunDiffuse();
00742     lightDir*= (float)lc.SunContribution * (color.R + color.G + color.B);
00743 
00744     // merge pointLights
00745     const CVector       &modelPos= sc->getWorldMatrix().getPos();
00746     for(uint i=0;i<NL3D_MAX_LIGHT_CONTRIBUTION;i++)
00747     {
00748         CPointLight     *pl= lc.PointLight[i];
00749         // End of List?
00750         if(!pl)
00751             break;
00752 
00753         CVector plDir= modelPos - pl->getPosition();
00754         plDir.normalize();
00755         // Sum with this light, weighted by AttFactor, and light color
00756         color= pl->getDiffuse();
00757         lightDir+= plDir * (float)lc.AttFactor[i] * (float)(color.R + color.G + color.B);
00758     }
00759 
00760     // normalize merged dir
00761     lightDir.normalize();
00762 
00763     // clamp the light direction in z, according to Caster restriction
00764     float   zThre= sc->getShadowMapDirectionZThreshold();
00765     if(lightDir.z>zThre)
00766     {
00767         /* normalize the x/y component so z=zthre
00768             we want this: sqrt(x2+y2+z2)==1, which solve for x2+y2= 1-z2
00769             the scale to apply to x and y is therefore deduced from:
00770                     sqr(scale)=(1-z2)/(x2+y2)
00771         */
00772         float   scale= 0.f;
00773         if(lightDir.x!=0.f || lightDir.y!=0.f)
00774             scale= sqrtf( (1-sqr(zThre)) / (sqr(lightDir.x)+sqr(lightDir.y)) );
00775         lightDir.x*= scale;
00776         lightDir.y*= scale;
00777         // force z component to be at least zthre
00778         lightDir.z= zThre;
00779 
00780         // re-normalize in case of precision problems
00781         lightDir.normalize();
00782     }
00783 }
00784 
00785 
00786 // ***************************************************************************
00787 void            CShadowMapManager::computeShadowColors(CScene *scene, CTransform *sc, CRGBA &ambient, CRGBA &diffuse)
00788 {
00789     const   CLightContribution  &lc= sc->getLightContribution();
00790 
00791     // Get the current ambiant
00792     ambient= lc.computeCurrentAmbient(scene->getSunAmbient());
00793 
00794     // Compute the current diffuse as a sum (not a mean)
00795     uint    r, g, b;
00796     CRGBA   color= scene->getSunDiffuse();
00797     r= color.R * lc.SunContribution;
00798     g= color.G * lc.SunContribution;
00799     b= color.B * lc.SunContribution;
00800 
00801     // Add PointLights contribution
00802     for(uint i=0;i<NL3D_MAX_LIGHT_CONTRIBUTION;i++)
00803     {
00804         CPointLight     *pl= lc.PointLight[i];
00805         // End of List?
00806         if(!pl)
00807             break;
00808 
00809         // Sum with this light, weighted by AttFactor
00810         color= pl->getDiffuse();
00811         r+= color.R * lc.AttFactor[i];
00812         g+= color.G * lc.AttFactor[i];
00813         b+= color.B * lc.AttFactor[i];
00814     }
00815 
00816     // normalize
00817     r>>=8;
00818     g>>=8;
00819     b>>=8;
00820 
00821     // Don't take the MergedPointLight into consideration (should add to the diffuse part here, but rare case)
00822 
00823     diffuse.R= uint8(min(r, 255U));
00824     diffuse.G= uint8(min(g, 255U));
00825     diffuse.B= uint8(min(b, 255U));
00826 }
00827 
00828 
00829 // ***************************************************************************
00830 void            CShadowMapManager::fillBlackBorder(IDriver *drv, uint numPassText, uint numTextW, uint numTextH, uint baseTextureSize)
00831 {
00832     if(numPassText==0)
00833         return;
00834 
00835     // the number of lines that have all their column disp.
00836     uint    numFullLine= numPassText/numTextW;
00837     // for the last line not full, the number of column setuped
00838     uint    lastLineNumCol=  numPassText - (numFullLine*numTextW);
00839     // number of line including the last line if not empty
00840     uint    numTotalLine= numFullLine + (lastLineNumCol?1:0);
00841 
00842     // Compute how many quads to render
00843     uint    numHQuads= numTotalLine * 2;
00844     uint    numTotalCol;
00845     uint    numVQuads;
00846     if(numFullLine)
00847         numTotalCol= numTextW;
00848     else
00849         numTotalCol= lastLineNumCol;
00850     numVQuads= numTotalCol * 2;
00851 
00852     _FillQuads.setNumVertices((numVQuads + numHQuads)*4);
00853 
00854     // Fill HQuads.
00855     uint    i;
00856     for(i=0;i<numTotalLine;i++)
00857     {
00858         uint    w;
00859         if(i<numFullLine)
00860             w= numTextW*baseTextureSize;
00861         else
00862             w= lastLineNumCol*baseTextureSize;
00863         // bottom of text
00864         setBlackQuad(i*2+0, 0, i*baseTextureSize, w, 1);
00865         // top of text
00866         setBlackQuad(i*2+1, 0, (i+1)*baseTextureSize-1, w, 1);
00867     }
00868 
00869     // Fill VQuads;
00870     uint    baseId= numTotalLine*2;
00871     for(i=0;i<numTotalCol;i++)
00872     {
00873         uint    h;
00874         if(i<lastLineNumCol)
00875             h= numTotalLine*baseTextureSize;
00876         else
00877             h= numFullLine*baseTextureSize;
00878         // left of text
00879         setBlackQuad(baseId + i*2+0, i*baseTextureSize, 0, 1, h);
00880         // right of text
00881         setBlackQuad(baseId + i*2+1, (i+1)*baseTextureSize-1, 0, 1, h);
00882     }
00883 
00884     // Render Quads
00885     _FillMaterial.setColor(CRGBA(0,0,0,0));
00886     drv->activeVertexBuffer(_FillQuads);
00887     drv->renderRawQuads(_FillMaterial, 0, numHQuads+numVQuads);
00888 }
00889 
00890 
00891 // ***************************************************************************
00892 void            CShadowMapManager::setBlackQuad(uint index, sint x, sint y, sint w, sint h)
00893 {
00894     float   x0= (float)x;
00895     float   y0= (float)y;
00896     float   x1= (float)x+(float)w;
00897     float   y1= (float)y+(float)h;
00898     index*= 4;
00899     CVertexBufferReadWrite vba;
00900     _FillQuads.lock(vba);
00901     vba.setVertexCoord (index+0, CVector (x0, 0, y0));
00902     vba.setVertexCoord (index+1, CVector (x1, 0, y0));
00903     vba.setVertexCoord (index+2, CVector (x1, 0, y1));
00904     vba.setVertexCoord (index+3, CVector (x0, 0, y1));
00905 }
00906 
00907 // ***************************************************************************
00908 void            CShadowMapManager::updateBlurTexture(IDriver &drv, uint w, uint h)
00909 {
00910     w= max(w, 2U);
00911     h= max(h, 2U);
00912     // if same size than setup, quit
00913     if(_BlurTextureW==w && _BlurTextureH==h)
00914         return;
00915 
00916     // release old SmartPtr
00917     uint i, j;
00918     for (i=0; i<2; i++)
00919     {
00920         _BlurMaterial[i].setTexture(0, NULL);
00921         _BlurMaterial[i].setTexture(1, NULL);
00922         _BlurMaterial[i].setTexture(2, NULL);
00923         _BlurMaterial[i].setTexture(3, NULL);
00924     }
00925     _BlurTexture[0]= NULL;
00926     _BlurTexture[1]= NULL;
00927     _BlurTextureW= w;
00928     _BlurTextureH= h;
00929     // NB: the format must be RGBA; else slow copyFrameBufferToTexture()
00930     for (i=0; i<2; i++)
00931     {
00932         uint8   *tmpMem= new uint8[4*_BlurTextureW*_BlurTextureH];
00933         _BlurTexture[i] = new CTextureMem (tmpMem, 4*_BlurTextureW*_BlurTextureH, true, false, _BlurTextureW, _BlurTextureH);
00934         _BlurTexture[i]->setWrapS (ITexture::Clamp);
00935         _BlurTexture[i]->setWrapT (ITexture::Clamp);
00936         _BlurTexture[i]->setFilterMode (ITexture::Linear, ITexture::LinearMipMapOff);
00937         _BlurTexture[i]->generate();
00938         _BlurTexture[i]->setReleasable (false);
00939         _BlurTexture[i]->setRenderTarget (true);
00940         // For Texture Profiling
00941         _BlurTexture[i]->setTextureCategory(_TextureCategory);
00942     }
00943 
00944     uint maxNumCstLighted;
00945     uint maxNumCstUnlighted;
00946     drv.getNumPerStageConstant(maxNumCstLighted, maxNumCstUnlighted);
00947 
00948     // set to the material
00949     for (i=0; i<2; i++)
00950     {
00951         for (j=0; j<maxNumCstUnlighted; j++)
00952         {
00953             _BlurMaterial[i].setTexture(j, _BlurTexture[i]);
00954         }
00955     }
00956 
00957     // compute values for texturing
00958     _BlurTextureOOW= 1.f / _BlurTextureW;
00959     _BlurTextureOOH= 1.f / _BlurTextureH;
00960     // The Delta HalfPixel
00961     _BlurTextureD05W= 0.5f*_BlurTextureOOW;
00962     _BlurTextureD05H= 0.5f*_BlurTextureOOH;
00963 }
00964 
00965 
00966 // ***************************************************************************
00967 void            CShadowMapManager::copyScreenToBlurTexture(IDriver *drv, uint numPassText, uint numTextW, uint numTextH, uint baseTextureSize)
00968 {
00969     if(numPassText==0)
00970         return;
00971 
00972     // TODO_SHADOW: optim: split into 2 copy for less pixel draw on the last line? No because of OverHead?
00973 
00974     // number of line including the last line if not empty
00975 //  uint    numTotalLine= (numPassText+numTextW-1)/numTextW;
00976     // number of column.
00977 //  uint    numTotalCol= (numPassText<numTextW)?numPassText:numTextW;
00978 
00979     /* todo hulud shadows
00980     drv->copyFrameBufferToTexture(_BlurTexture, 0, 0, 0, 0, 0, numTotalCol*baseTextureSize, numTotalLine*baseTextureSize); */
00981 }
00982 
00983 // ***************************************************************************
00984 void            CShadowMapManager::applyFakeGaussianBlur(IDriver *drv, uint numPassText, uint numTextW, uint numTextH, uint baseTextureSize, uint blurSource)
00985 {
00986     if(numPassText==0)
00987         return;
00988 
00989     // the number of lines that have all their column disp.
00990     uint    numFullLine= numPassText/numTextW;
00991     // for the last line not full, the number of column setuped
00992     uint    lastLineNumCol=  numPassText - (numFullLine*numTextW);
00993 
00994     // Split into 2 quads. one for the first full lines, and one for the last not full line.
00995     uint    index= 0;
00996     if(numFullLine)
00997         setBlurQuadFakeGaussian(index++, 0, 0, numTextW*baseTextureSize, numFullLine*baseTextureSize);
00998     if(lastLineNumCol)
00999         setBlurQuadFakeGaussian(index++, 0, numFullLine*baseTextureSize, lastLineNumCol*baseTextureSize, baseTextureSize);
01000 
01001     // render
01002     drv->activeVertexBuffer(_BlurQuads);
01003     drv->renderRawQuads(_BlurMaterial[blurSource], 0, index);
01004 }
01005 
01006 
01007 // ***************************************************************************
01008 void            CShadowMapManager::setBlurQuadFakeGaussian(uint index, sint x, sint y, sint w, sint h)
01009 {
01010     float   x0= (float)x;
01011     float   y0= (float)y;
01012     float   x1= (float)x+(float)w;
01013     float   y1= (float)y+(float)h;
01014     float   u0= x0*_BlurTextureOOW;
01015     float   v0= y0*_BlurTextureOOH;
01016     float   u1= x1*_BlurTextureOOW;
01017     float   v1= y1*_BlurTextureOOH;
01018     index*= 4;
01019 
01020     // NB: the order of the Delta (--,++,-+,+-) is made so it works well with 2,3 or 4 texture support.
01021 
01022     // vertex 0
01023     CVertexBufferReadWrite vba;
01024     _BlurQuads.lock(vba);
01025     vba.setVertexCoord (index+0, CVector (x0, 0, y0));
01026     vba.setTexCoord(index+0, 0, u0-_BlurTextureD05W, v0-_BlurTextureD05H);
01027     vba.setTexCoord(index+0, 1, u0+_BlurTextureD05W, v0+_BlurTextureD05H);
01028     vba.setTexCoord(index+0, 2, u0-_BlurTextureD05W, v0+_BlurTextureD05H);
01029     vba.setTexCoord(index+0, 3, u0+_BlurTextureD05W, v0-_BlurTextureD05H);
01030     // vertex 1
01031     vba.setVertexCoord (index+1, CVector (x1, 0, y0));
01032     vba.setTexCoord(index+1, 0, u1-_BlurTextureD05W, v0-_BlurTextureD05H);
01033     vba.setTexCoord(index+1, 1, u1+_BlurTextureD05W, v0+_BlurTextureD05H);
01034     vba.setTexCoord(index+1, 2, u1-_BlurTextureD05W, v0+_BlurTextureD05H);
01035     vba.setTexCoord(index+1, 3, u1+_BlurTextureD05W, v0-_BlurTextureD05H);
01036     // vertex 2
01037     vba.setVertexCoord (index+2, CVector (x1, 0, y1));
01038     vba.setTexCoord(index+2, 0, u1-_BlurTextureD05W, v1-_BlurTextureD05H);
01039     vba.setTexCoord(index+2, 1, u1+_BlurTextureD05W, v1+_BlurTextureD05H);
01040     vba.setTexCoord(index+2, 2, u1-_BlurTextureD05W, v1+_BlurTextureD05H);
01041     vba.setTexCoord(index+2, 3, u1+_BlurTextureD05W, v1-_BlurTextureD05H);
01042     // vertex 3
01043     vba.setVertexCoord (index+3, CVector (x0, 0, y1));
01044     vba.setTexCoord(index+3, 0, u0-_BlurTextureD05W, v1-_BlurTextureD05H);
01045     vba.setTexCoord(index+3, 1, u0+_BlurTextureD05W, v1+_BlurTextureD05H);
01046     vba.setTexCoord(index+3, 2, u0-_BlurTextureD05W, v1+_BlurTextureD05H);
01047     vba.setTexCoord(index+3, 3, u0+_BlurTextureD05W, v1-_BlurTextureD05H);
01048 }
01049 
01050 
01051 // ***************************************************************************
01052 struct  CShadowMapSort
01053 {
01054     CTransform      *Caster;
01055     float           Weight;
01056 
01057     bool        operator<(const CShadowMapSort &o) const
01058     {
01059         return Weight<o.Weight;
01060     }
01061 };
01062 
01063 // ***************************************************************************
01064 void            CShadowMapManager::selectShadowMapsToGenerate(CScene *scene)
01065 {
01066     // TODO: Scene option.
01067     const uint      maxPerFrame= 8;
01068     const float     minCamDist= 10;
01069     const CVector   &camPos= scene->getRenderTrav().CamPos;
01070     uint            i;
01071 
01072     // **** Clear first
01073     clearGenerateShadowCasters();
01074 
01075     // If the scene filter skeleton render, suppose no generation at all. Ugly.
01076     if(! (scene->getFilterRenderFlags() & UScene::FilterSkeleton) )
01077         return;
01078 
01079     // **** Select
01080     // For all ShadowCaster inserted
01081     static vector<CShadowMapSort>       sortList;
01082     sortList.clear();
01083     sortList.reserve(_ShadowCasters.size());
01084     for(i=0;i<_ShadowCasters.size();i++)
01085     {
01086         CTransform  *caster= _ShadowCasters[i];
01087         /* If the shadowMap exist, and if not totaly faded
01088             NB: take FinalFade here because if 1, it won't be rendered in renderProject()
01089             so don't really need to update (useful for update reason, but LastGenerationFrame do the job)
01090         */
01091         if(caster->getShadowMap() && caster->getShadowMap()->getFinalFade()<1 )
01092         {
01093             CShadowMapSort      sms;
01094             sms.Caster= caster;
01095             // The Weight is the positive delta of frame
01096             sms.Weight= (float)(scene->getNumRender() - caster->getShadowMap()->LastGenerationFrame);
01097             // Modulated by Caster Distance from Camera.
01098             float   distToCam= (caster->getWorldMatrix().getPos() - camPos).norm();
01099             distToCam= max(distToCam, minCamDist);
01100             // The farthest, the less important
01101             sms.Weight/= distToCam;
01102 
01103             // Append
01104             sortList.push_back(sms);
01105         }
01106     }
01107 
01108     // Sort increasing
01109     sort(sortList.begin(), sortList.end());
01110 
01111     // Select the best
01112     uint    numSel= min((uint)sortList.size(), maxPerFrame);
01113     _GenerateShadowCasters.resize(numSel);
01114     for(i= 0;i<numSel;i++)
01115     {
01116         _GenerateShadowCasters[i]= sortList[sortList.size()-1-i].Caster;
01117     }
01118 
01119     // **** Flag selecteds
01120     // For All selected models, indicate that they will generate shadowMap for this Frame.
01121     for(i=0;i<_GenerateShadowCasters.size();i++)
01122     {
01123         _GenerateShadowCasters[i]->setGeneratingShadowMap(true);
01124     }
01125 }
01126 
01127 
01128 // ***************************************************************************
01129 void            CShadowMapManager::clearAllShadowCasters()
01130 {
01131     _ShadowReceiverGrid.clear();
01132     _ShadowCasters.clear();
01133     clearGenerateShadowCasters();
01134 }
01135 
01136 
01137 // ***************************************************************************
01138 void            CShadowMapManager::clearGenerateShadowCasters()
01139 {
01140     // Reset first each flag of all models
01141     for(uint i=0;i<_GenerateShadowCasters.size();i++)
01142     {
01143         _GenerateShadowCasters[i]->setGeneratingShadowMap(false);
01144     }
01145 
01146     _GenerateShadowCasters.clear();
01147 }
01148 
01149 // ***************************************************************************
01150 ITexture        *CShadowMapManager::allocateTexture(uint textSize)
01151 {
01152     nlassert( isPowerOf2(textSize) );
01153 
01154     // **** First, find a free texture already allocated.
01155     if(!_FreeShadowTextures.empty())
01156     {
01157         ITexture    *freeText= _FreeShadowTextures.back()->second;
01158         // If Ok for the size.
01159         if(freeText->getWidth() == textSize)
01160         {
01161             // take this texture => no more free.
01162             _FreeShadowTextures.pop_back();
01163             return freeText;
01164         }
01165         // else, suppose that we still take this slot.
01166         else
01167         {
01168             // but since bad size, delete this slot from the map and create a new one (below)
01169             _ShadowTextureMap.erase(_FreeShadowTextures.back());
01170             // no more valid it
01171             _FreeShadowTextures.pop_back();
01172         }
01173     }
01174 
01175     // **** Else Allocate new one.
01176     // NB: the format must be RGBA; else slow copyFrameBufferToTexture()
01177     uint8   *tmpMem= new uint8[4*textSize*textSize];
01178     ITexture    *text;
01179     text = new CTextureMem (tmpMem, 4*textSize*textSize, true, false, textSize, textSize);
01180     text->setWrapS (ITexture::Clamp);
01181     text->setWrapT (ITexture::Clamp);
01182     text->setFilterMode (ITexture::Linear, ITexture::LinearMipMapOff);
01183     text->generate();
01184     text->setReleasable (false);
01185     text->setRenderTarget (true);
01186     // For Texture Profiling
01187     text->setTextureCategory(_TextureCategory);
01188 
01189     // Setup in the map.
01190     _ShadowTextureMap[text]= text;
01191 
01192     return text;
01193 }
01194 
01195 // ***************************************************************************
01196 void            CShadowMapManager::releaseTexture(ITexture *text)
01197 {
01198     if(!text)
01199         return;
01200 
01201     ItTextureMap    it= _ShadowTextureMap.find(text);
01202     nlassert(it!=_ShadowTextureMap.end());
01203 
01204     // Don't release it, but insert in Free Space
01205     _FreeShadowTextures.push_back(it);
01206 }
01207 
01208 // ***************************************************************************
01209 void            CShadowMapManager::garbageShadowTextures(CScene *scene)
01210 {
01211     uint    defSize= scene->getShadowMapTextureSize();
01212 
01213     // For all Free Textures only, release the one that are no more of the wanted default ShadowMap Size.
01214     std::vector<ItTextureMap>::iterator     itVec= _FreeShadowTextures.begin();
01215     for(;itVec!=_FreeShadowTextures.end();)
01216     {
01217         if((*itVec)->second->getWidth() != defSize)
01218         {
01219             // release the map texture iterator
01220             _ShadowTextureMap.erase(*itVec);
01221             // release the Vector Free iterator.
01222             itVec= _FreeShadowTextures.erase(itVec);
01223         }
01224         else
01225         {
01226             itVec++;
01227         }
01228     }
01229 
01230     // For memory optimisation, allow only a small extra of Texture allocated.
01231     if(_FreeShadowTextures.size()>NL3D_SMM_MAX_FREETEXT)
01232     {
01233         // Release the extra texture (Hysteresis: divide by 2 the max wanted free to leave)
01234         uint    startToFree= NL3D_SMM_MAX_FREETEXT/2;
01235         for(uint i=startToFree;i<_FreeShadowTextures.size();i++)
01236         {
01237             // Free the texture entry.
01238             _ShadowTextureMap.erase(_FreeShadowTextures[i]);
01239         }
01240         // resize vector
01241         _FreeShadowTextures.resize(startToFree);
01242     }
01243 }
01244 
01245 
01246 } // NL3D
01247 

Generated on Thu Jan 7 08:26:30 2010 for NeL by  doxygen 1.6.1