Handling multiple depth textures is very easy. I generate 78 (most of them low-res!) and allocate them based on distance between the projection and camera.
I made a struct to keep things tidy:
- Code: Select all
struct DepthTexCache {
CUtlVector<CTextureReference> Textures;
CUtlVector<bool> Locks;
int Resolution;
} DepthTexCache_t;
Then added an array of it to CClientShadowMgr (I ended up not using the Huge size):
- Code: Select all
DepthTexCache m_DepthTextureCaches[4]; // Huge, Large, Small, Tiny
The render targets are generated in CClientShadowMgr::InitDepthTextureShadows()...
- Code: Select all
// Create some number of depth-stencil textures
int nTextureResolutions[] = { 2048, 1024, 512, 256 };
int nTexturesPerResolution[] = { 0, 18, 24, 36 }; // 0, 3, 4, 6 omnis...but individual envprojtex'es will eat into that
int numInits = 0;
for (int j=0; j < 4; j++)
{
m_DepthTextureCaches[j].Textures.Purge();
m_DepthTextureCaches[j].Locks.Purge();
m_DepthTextureCaches[j].Resolution = nTextureResolutions[j];
for( int i=0; i < nTexturesPerResolution[j]; i++ )
{
CTextureReference depthTex; // Depth-stencil surface
bool bFalse = false;
char strRTName[64];
sprintf( strRTName, "_rt_ShadowDepthTexture_%d", numInits );
numInits++;
depthTex.InitRenderTarget( nTextureResolutions[j], nTextureResolutions[j], RT_SIZE_NO_CHANGE, dstFormat, MATERIAL_RT_DEPTH_ONLY, false, strRTName );
m_DepthTextureCaches[j].Textures.AddToTail( depthTex );
m_DepthTextureCaches[j].Locks.AddToTail( bFalse );
}
}
...and allocated each frame in CClientShadowMgr::LockShadowDepthTexture():
- Code: Select all
int CClientShadowMgr::LockShadowDepthTexture( CTextureReference *shadowDepthTexture, float dist )
{
int StartCache = 3;
if (dist < 512 )
StartCache = 1;
else if (dist < 1024)
StartCache = 2;
for (int i = StartCache; i < 4; i++)
for ( int j = 0; j < m_DepthTextureCaches[i].Textures.Count(); j++ )
{
if ( m_DepthTextureCaches[i].Locks[j] == false && m_DepthTextureCaches[i].Textures[j].IsValid() ) // is it free?
{
*shadowDepthTexture = m_DepthTextureCaches[i].Textures[j];
m_DepthTextureCaches[i].Locks[j] = true;
return m_DepthTextureCaches[i].Resolution;
}
}
return 0;
}
Edit: not forgetting destruction...
- Code: Select all
void CClientShadowMgr::ShutdownDepthTextureShadows()
{
if( m_bDepthTexturesAllocated )
{
// Shut down the dummy texture
m_DummyColorTexture.Shutdown();
for (int i=0; i < 4; i++)
while( m_DepthTextureCaches[i].Textures.Count() )
{
int elem = m_DepthTextureCaches[i].Textures.Count() - 1;
m_DepthTextureCaches[i].Textures.Tail().Shutdown();
m_DepthTextureCaches[i].Locks.Remove( elem );
m_DepthTextureCaches[i].Textures.Remove( elem );
}
m_bDepthTexturesAllocated = false;
}
m_bDepthTextureActive = false;
}
Edit: I also forgot to post the code which unlocks the textures.
- Code: Select all
void CClientShadowMgr::UnlockAllShadowDepthTextures()
{
for (int j=0; j < 4; j++)
for (int i=0; i< m_DepthTextureCaches[j].Locks.Count(); i++ )
{
m_DepthTextureCaches[j].Locks[i] = false;
}
shadowmgr->SetSinglePassFlashlightRenderState( SHADOW_HANDLE_INVALID );
shadowmgr->PopSinglePassFlashlightStateEnabled();
}
And that's it, no entity modification needed. Pleasantly, I've not noticed any popping between LODs.
Problems:
- There is currently a large hitch when the depth texture transitions to a fresh render target, but it should be easy to fix when I get round to it.
- More concerning is the need to disable r_flashlightscissor if you want to display more than one projected texture at once, as this greatly increases overdraw (especially if you have omnidirectional projections!) and can send performance off a cliff. Not sure if this can even be fixed in mode code.