Dynamic RTT shadow angles in Source 2007/Clientshadowmgr.cpp: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
No edit summary
m (→‎top: clean up, added uncategorised, deadend tags)
 
(2 intermediate revisions by one other user not shown)
Line 1: Line 1:
<source lang=cpp>
{{Dead end|date=January 2024}}
<source lang="cpp">
//========= Copyright Valve Corporation, All rights reserved. ============//
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//
Line 55: Line 56:
// the shadow from all studio models
// the shadow from all studio models
//===========================================================================//
//===========================================================================//


#include "cbase.h"
#include "cbase.h"
Line 91: Line 91:


static IVEngineServer *g_pEngineServer = nullptr;
static IVEngineServer *g_pEngineServer = nullptr;
static CWorldLights s_WorldLights;
CWorldLights *g_pWorldLights = &s_WorldLights;


//-----------------------------------------------------------------------------
// memdbgon must be the last include file in a .cpp file!!!
// Purpose: Calculate intensity ratio for a worldlight by distance
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
static float Engine_WorldLightDistanceFalloff( const dworldlight_t *wl, const Vector &delta )
{
float falloff;


switch ( wl->type )
static ConVar r_flashlightdrawfrustum( "r_flashlightdrawfrustum", "0" );
{
static ConVar r_flashlightmodels( "r_flashlightmodels", "1" );
case emit_surface:
static ConVar r_shadowrendertotexture( "r_shadowrendertotexture", "0" );
// Cull out stuff that's too far
static ConVar r_flashlight_version2( "r_flashlight_version2", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
if ( wl->radius != 0 )
void WorldLightCastShadowCallback( IConVar *pVar, const char *pszOldValue, float flOldValue );
{
static ConVar r_worldlight_castshadows( "r_worldlight_castshadows", "1", FCVAR_CHEAT, "Allow world lights to cast shadows", true, 0, true, 1, WorldLightCastShadowCallback );
if ( DotProduct( delta, delta ) > ( wl->radius * wl->radius ) )
static ConVar r_worldlight_lerptime( "r_worldlight_lerptime", "0.5", FCVAR_CHEAT );
return 0.0f;
static ConVar r_worldlight_debug( "r_worldlight_debug", "0", FCVAR_CHEAT );
}
static ConVar r_worldlight_shortenfactor( "r_worldlight_shortenfactor", "2" , FCVAR_CHEAT, "Makes shadows cast from local lights shorter" );
static ConVar r_worldlight_mincastintensity( "r_worldlight_mincastintensity", "0.3", FCVAR_CHEAT, "Minimum brightness of a light to be classed as shadow casting", true, 0, false, 0 );


return InvRSquared( delta );
ConVar r_flashlightdepthtexture( "r_flashlightdepthtexture", "1" );


case emit_skylight:
#if defined( _X360 )
return 1.0f;
ConVar r_flashlightdepthres( "r_flashlightdepthres", "512" );
#else
ConVar r_flashlightdepthres( "r_flashlightdepthres", "1024" );
#endif


case emit_quakelight:
ConVar r_threaded_client_shadow_manager( "r_threaded_client_shadow_manager", "0" );
// X - r;
falloff = wl->linear_attn - FastSqrt( DotProduct( delta, delta ) );
if ( falloff < 0 )
return 0.0f;


return falloff;
#ifdef _WIN32
#pragma warning( disable: 4701 )
#endif


case emit_skyambient:
// forward declarations
return 1.0f;
void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );
 
case emit_point:
case emit_spotlight: // Directional & positional
{
float dist2, dist;
 
dist2 = DotProduct( delta, delta );
dist = FastSqrt( dist2 );
 
// Cull out stuff that's too far
if ( wl->radius != 0 && dist > wl->radius )
return 0.0f;
 
return 1.0f / ( wl->constant_attn + wl->linear_attn * dist + wl->quadratic_attn * dist2 );
}
}
 
return 1.0f;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Initialize game system and members
// A texture allocator used to batch textures together
// At the moment, the implementation simply allocates blocks of max 256x256
// and each block stores an array of uniformly-sized textures
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CWorldLights::CWorldLights() : CAutoGameSystem( "World lights" )
typedef unsigned short TextureHandle_t;
enum
{
{
m_nWorldLights = 0;
INVALID_TEXTURE_HANDLE = (TextureHandle_t)~0
m_pWorldLights = nullptr;
};
}


//-----------------------------------------------------------------------------
class CTextureAllocator
// Purpose: Clear worldlights, free memory
//-----------------------------------------------------------------------------
void CWorldLights::Clear()
{
{
m_nWorldLights = 0;
public:
// Initialize the allocator with something that knows how to refresh the bits
void Init();
void Shutdown();


if ( m_pWorldLights )
// Resets the allocator
{
void Reset();
delete[] m_pWorldLights;
m_pWorldLights = nullptr;
}
}


//-----------------------------------------------------------------------------
// Deallocates everything
// Purpose: Get the IVEngineServer, we need this for the PVS functions
void DeallocateAllTextures();
//-----------------------------------------------------------------------------
bool CWorldLights::Init()
{
factorylist_t factories;
FactoryList_Retrieve( factories );


if ( ( g_pEngineServer = static_cast<IVEngineServer *>( factories.appSystemFactory( INTERFACEVERSION_VENGINESERVER, nullptr ) ) ) == nullptr )
// Allocate, deallocate texture
return false;
TextureHandle_t AllocateTexture( int w, int h );
void DeallocateTexture( TextureHandle_t h );


return true;
// Mark texture as being used... (return true if re-render is needed)
}
bool UseTexture( TextureHandle_t h, bool bWillRedraw, float flArea );
bool HasValidTexture( TextureHandle_t h );


//-----------------------------------------------------------------------------
// Advance frame...
// Purpose: Get all world lights from the BSP
void AdvanceFrame();
//-----------------------------------------------------------------------------
void CWorldLights::LevelInitPreEntity()
{
// Get the map path
const char *pszMapName = modelinfo->GetModelName( modelinfo->GetModel( 1 ) );


// Open map
// Get at the location of the texture
FileHandle_t hFile = g_pFullFileSystem->Open( pszMapName, "rb" );
void GetTextureRect(TextureHandle_t handle, int& x, int& y, int& w, int& h );
if ( !hFile )
{
Warning( "CWorldLights: Unable to open map\n" );
return;
}


// Read the BSP header. We don't need to do any version checks, etc. as we
// Get at the texture it's a part of
// can safely assume that the engine did this for us
ITexture *GetTexture();
dheader_t hdr;
g_pFullFileSystem->Read( &hdr, sizeof( hdr ), hFile );
// Get at the total texture size.
 
void GetTotalTextureSize( int& w, int& h );
// Grab the light lump and seek to it
lump_t &lightLump = hdr.lumps[LUMP_WORLDLIGHTS];


// If the worldlights lump is empty, that means theres no normal, LDR lights to extract
void DebugPrintCache( void );
// This can happen when, for example, the map is compiled in HDR mode only
 
// So move on to the HDR worldlights lump
private:
if ( lightLump.filelen == 0 )
typedef unsigned short FragmentHandle_t;
 
enum
{
{
lightLump = hdr.lumps[LUMP_WORLDLIGHTS_HDR];
INVALID_FRAGMENT_HANDLE = (FragmentHandle_t)~0,
}
TEXTURE_PAGE_SIZE     = 1024,
MAX_TEXTURE_POWER    = 8,
#if !defined( _X360 )
MIN_TEXTURE_POWER     = 4,
#else
MIN_TEXTURE_POWER     = 5, // per resolve requirements to ensure 32x32 aligned offsets
#endif
MAX_TEXTURE_SIZE     = (1 << MAX_TEXTURE_POWER),
MIN_TEXTURE_SIZE     = (1 << MIN_TEXTURE_POWER),
BLOCK_SIZE     = MAX_TEXTURE_SIZE,
BLOCKS_PER_ROW     = (TEXTURE_PAGE_SIZE / MAX_TEXTURE_SIZE),
BLOCK_COUNT     = (BLOCKS_PER_ROW * BLOCKS_PER_ROW),
};


// If we can't divide the lump data into a whole number of worldlights,
struct TextureInfo_t
// then the BSP format changed and we're unaware
if ( lightLump.filelen % sizeof( dworldlight_t ) )
{
{
Warning( "CWorldLights: Unknown world light lump\n" );
FragmentHandle_t m_Fragment;
unsigned short m_Size;
unsigned short m_Power;
};


// Close file
struct FragmentInfo_t
g_pFullFileSystem->Close( hFile );
{
return;
unsigned short m_Block;
}
unsigned short m_Index;
TextureHandle_t m_Texture;


g_pFullFileSystem->Seek( hFile, lightLump.fileofs, FILESYSTEM_SEEK_HEAD );
// Makes sure we don't overflow
unsigned int m_FrameUsed;
};


// Allocate memory for the worldlights
struct BlockInfo_t
m_nWorldLights = lightLump.filelen / sizeof( dworldlight_t );
{
m_pWorldLights = new dworldlight_t[m_nWorldLights];
unsigned short m_FragmentPower;
};


// Read worldlights then close
struct Cache_t
g_pFullFileSystem->Read( m_pWorldLights, lightLump.filelen, hFile );
g_pFullFileSystem->Close( hFile );
 
DevMsg( "CWorldLights: Load successful (%d lights at 0x%p)\n", m_nWorldLights, m_pWorldLights );
}
 
//-----------------------------------------------------------------------------
// Purpose: Find the brightest light source at a point
//-----------------------------------------------------------------------------
bool CWorldLights::GetBrightestLightSource( const Vector &vecPosition, Vector &vecLightPos, Vector &vecLightBrightness )
{
if ( !m_nWorldLights || !m_pWorldLights )
return false;
 
// Default light position and brightness to zero
vecLightBrightness.Init();
vecLightPos.Init();
 
// Find the size of the PVS for our current position
int nCluster = g_pEngineServer->GetClusterForOrigin( vecPosition );
int nPVSSize = g_pEngineServer->GetPVSForCluster( nCluster, 0, nullptr );
 
// Get the PVS at our position
byte *pvs = new byte[nPVSSize];
g_pEngineServer->GetPVSForCluster( nCluster, nPVSSize, pvs );
 
// Iterate through all the worldlights
for ( int i = 0; i < m_nWorldLights; ++i )
{
{
dworldlight_t *light = &m_pWorldLights[i];
unsigned short m_List;
};


// Skip skyambient
// Adds a block worth of fragments to the LRU
if ( light->type == emit_skyambient )
void AddBlockToLRU( int block );
{
//engine->Con_NPrintf( i, "%d: skyambient", i );
continue;
}


// Handle sun
// Unlink fragment from cache
if ( light->type == emit_skylight )
void UnlinkFragmentFromCache( Cache_t& cache, FragmentHandle_t fragment );
{
// Calculate sun position
Vector vecAbsStart = vecPosition + Vector( 0, 0, 30 );
Vector vecAbsEnd = vecAbsStart - ( light->normal * MAX_TRACE_LENGTH );


trace_t tr;
// Mark something as being used (MRU)..
UTIL_TraceLine( vecPosition, vecAbsEnd, MASK_OPAQUE, nullptr, COLLISION_GROUP_NONE, &tr );
void MarkUsed( FragmentHandle_t fragment );


// If we didn't hit anything then we have a problem
// Mark something as being unused (LRU)..
if ( !tr.DidHit() )
void MarkUnused( FragmentHandle_t fragment );
{
//engine->Con_NPrintf( i, "%d: skylight: couldn't touch sky", i );
continue;
}


// If we did hit something, and it wasn't the skybox, then skip
// Disconnect texture from fragment
// this worldlight
void DisconnectTextureFromFragment( FragmentHandle_t f );
if ( !( tr.surface.flags & SURF_SKY ) && !( tr.surface.flags & SURF_SKY2D ) )
{
//engine->Con_NPrintf( i, "%d: skylight: no sight to sun", i );
continue;
}


// Act like we didn't find any valid worldlights, so the shadow
// Returns the size of a particular fragment
// manager uses the default shadow direction instead (should be the
int GetFragmentPower( FragmentHandle_t f ) const;
// sun direction)


delete[] pvs;
// Stores the actual texture we're writing into
CTextureReference m_TexturePage;


return false;
CUtlLinkedList< TextureInfo_t, TextureHandle_t > m_Textures;
}
CUtlMultiList< FragmentInfo_t, FragmentHandle_t > m_Fragments;


// Calculate square distance to this worldlight
Cache_t m_Cache[MAX_TEXTURE_POWER+1];
Vector vecDelta = light->origin - vecPosition;
BlockInfo_t m_Blocks[BLOCK_COUNT];
float flDistSqr = vecDelta.LengthSqr();
unsigned int m_CurrentFrame;
float flRadiusSqr = light->radius * light->radius;
};


// Skip lights that are out of our radius
//-----------------------------------------------------------------------------
if ( flRadiusSqr > 0 && flDistSqr >= flRadiusSqr )
// Allocate/deallocate the texture page
{
//-----------------------------------------------------------------------------
//engine->Con_NPrintf( i, "%d: out-of-radius (dist: %d, radius: %d)", i, sqrt( flDistSqr ), light->radius );
void CTextureAllocator::Init()
continue;
{
}
for ( int i = 0; i <= MAX_TEXTURE_POWER; ++i )
{
m_Cache[i].m_List = m_Fragments.InvalidIndex();
}


// Is it out of our PVS?
#if !defined( _X360 )
if ( !g_pEngineServer->CheckOriginInPVS( light->origin, pvs, nPVSSize ) )
// don't need depth buffer for shadows
{
m_TexturePage.InitRenderTarget( TEXTURE_PAGE_SIZE, TEXTURE_PAGE_SIZE, RT_SIZE_NO_CHANGE, IMAGE_FORMAT_ARGB8888, MATERIAL_RT_DEPTH_NONE, false, "_rt_Shadows" );
//engine->Con_NPrintf( i, "%d: out of PVS", i );
#else
continue;
// unfortunate explicit management required for this render target
}
// 32bpp edram is only largest shadow fragment, but resolved to actual shadow atlas
// because full-res 1024x1024 shadow buffer is too large for EDRAM
m_TexturePage.InitRenderTargetTexture( TEXTURE_PAGE_SIZE, TEXTURE_PAGE_SIZE, RT_SIZE_NO_CHANGE, IMAGE_FORMAT_ARGB8888, MATERIAL_RT_DEPTH_NONE, false, "_rt_Shadows" );


// Calculate intensity at our position
// edram footprint is only 256x256x4 = 256K
float flRatio = Engine_WorldLightDistanceFalloff( light, vecDelta );
m_TexturePage.InitRenderTargetSurface( MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, IMAGE_FORMAT_ARGB8888, false );
Vector vecIntensity = light->intensity * flRatio;


// Is this light more intense than the one we already found?
// due to texture/surface size mismatch, ensure texture page is entirely cleared translucent
if ( vecIntensity.LengthSqr() <= vecLightBrightness.LengthSqr() )
// otherwise border artifacts at edge of shadows due to pixel shader averaging of unwanted bits
{
m_TexturePage->ClearTexture( 0, 0, 0, 0 );
//engine->Con_NPrintf( i, "%d: too dim", i );
#endif
continue;
}
}


// Can we see the light?
void CTextureAllocator::Shutdown()
trace_t tr;
{
Vector vecAbsStart = vecPosition + Vector( 0, 0, 30 );
m_TexturePage.Shutdown();
UTIL_TraceLine( vecAbsStart, light->origin, MASK_OPAQUE, nullptr, COLLISION_GROUP_NONE, &tr );
}
 
if ( tr.DidHit() )
{
//engine->Con_NPrintf( i, "%d: trace failed", i );
continue;
}
 
vecLightPos = light->origin;
vecLightBrightness = vecIntensity;
 
//engine->Con_NPrintf( i, "%d: set (%.2f)", i, vecIntensity.Length() );
}
 
delete[] pvs;
 
//engine->Con_NPrintf( m_nWorldLights, "result: %d", !vecLightBrightness.IsZero() );
return !vecLightBrightness.IsZero();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Singleton accessor
// Initialize the allocator with something that knows how to refresh the bits
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static CWorldLights s_WorldLights;
void CTextureAllocator::Reset()
CWorldLights *g_pWorldLights = &s_WorldLights;
{
DeallocateAllTextures();


// memdbgon must be the last include file in a .cpp file!!!
m_Textures.EnsureCapacity(256);
#include "tier0/memdbgon.h"
m_Fragments.EnsureCapacity(256);


static ConVar r_flashlightdrawfrustum( "r_flashlightdrawfrustum", "0" );
// Set up the block sizes....
static ConVar r_flashlightmodels( "r_flashlightmodels", "1" );
// FIXME: Improve heuristic?!?
static ConVar r_shadowrendertotexture( "r_shadowrendertotexture", "0" );
#if !defined( _X360 )
static ConVar r_flashlight_version2( "r_flashlight_version2", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
m_Blocks[0].m_FragmentPower  = MAX_TEXTURE_POWER-4; // 128 cells at ExE resolution
void WorldLightCastShadowCallback( IConVar *pVar, const char *pszOldValue, float flOldValue );
static ConVar r_worldlight_castshadows( "r_worldlight_castshadows", "1", FCVAR_CHEAT, "Allow world lights to cast shadows", true, 0, true, 1, WorldLightCastShadowCallback );
static ConVar r_worldlight_lerptime( "r_worldlight_lerptime", "0.5", FCVAR_CHEAT );
static ConVar r_worldlight_debug( "r_worldlight_debug", "0", FCVAR_CHEAT );
static ConVar r_worldlight_shortenfactor( "r_worldlight_shortenfactor", "2" , FCVAR_CHEAT, "Makes shadows cast from local lights shorter" );
static ConVar r_worldlight_mincastintensity( "r_worldlight_mincastintensity", "0.3", FCVAR_CHEAT, "Minimum brightness of a light to be classed as shadow casting", true, 0, false, 0 );
 
ConVar r_flashlightdepthtexture( "r_flashlightdepthtexture", "1" );
 
#if defined( _X360 )
ConVar r_flashlightdepthres( "r_flashlightdepthres", "512" );
#else
#else
ConVar r_flashlightdepthres( "r_flashlightdepthres", "1024" );
m_Blocks[0].m_FragmentPower  = MAX_TEXTURE_POWER-3; // 64 cells at DxD resolution
#endif
 
ConVar r_threaded_client_shadow_manager( "r_threaded_client_shadow_manager", "0" );
 
#ifdef _WIN32
#pragma warning( disable: 4701 )
#endif
#endif
m_Blocks[1].m_FragmentPower  = MAX_TEXTURE_POWER-3; // 64 cells at DxD resolution
m_Blocks[2].m_FragmentPower  = MAX_TEXTURE_POWER-2; // 32 cells at CxC resolution
m_Blocks[3].m_FragmentPower  = MAX_TEXTURE_POWER-2;
m_Blocks[4].m_FragmentPower  = MAX_TEXTURE_POWER-1; // 24 cells at BxB resolution
m_Blocks[5].m_FragmentPower  = MAX_TEXTURE_POWER-1;
m_Blocks[6].m_FragmentPower  = MAX_TEXTURE_POWER-1;
m_Blocks[7].m_FragmentPower  = MAX_TEXTURE_POWER-1;
m_Blocks[8].m_FragmentPower  = MAX_TEXTURE_POWER-1;
m_Blocks[9].m_FragmentPower  = MAX_TEXTURE_POWER-1;
m_Blocks[10].m_FragmentPower = MAX_TEXTURE_POWER; // 6 cells at AxA resolution
m_Blocks[11].m_FragmentPower = MAX_TEXTURE_POWER;
m_Blocks[12].m_FragmentPower = MAX_TEXTURE_POWER;
m_Blocks[13].m_FragmentPower = MAX_TEXTURE_POWER;
m_Blocks[14].m_FragmentPower = MAX_TEXTURE_POWER;
m_Blocks[15].m_FragmentPower = MAX_TEXTURE_POWER;


// forward declarations
// Initialize the LRU
void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );
int i;
for ( i = 0; i <= MAX_TEXTURE_POWER; ++i )
{
m_Cache[i].m_List = m_Fragments.CreateList();
}


// Now that the block sizes are allocated, create LRUs for the various block sizes
for ( i = 0; i < BLOCK_COUNT; ++i)
{
// Initialize LRU
AddBlockToLRU( i );
}


//-----------------------------------------------------------------------------
m_CurrentFrame = 0;
// A texture allocator used to batch textures together
}
// At the moment, the implementation simply allocates blocks of max 256x256
// and each block stores an array of uniformly-sized textures
//-----------------------------------------------------------------------------
typedef unsigned short TextureHandle_t;
enum
{
INVALID_TEXTURE_HANDLE = (TextureHandle_t)~0
};


class CTextureAllocator
void CTextureAllocator::DeallocateAllTextures()
{
{
public:
m_Textures.Purge();
// Initialize the allocator with something that knows how to refresh the bits
m_Fragments.Purge();
void Init();
for ( int i = 0; i <= MAX_TEXTURE_POWER; ++i )
void Shutdown();
{
m_Cache[i].m_List = m_Fragments.InvalidIndex();
}
}


// Resets the allocator
//-----------------------------------------------------------------------------
void Reset();
// Dump the state of the cache to debug out
//-----------------------------------------------------------------------------
void CTextureAllocator::DebugPrintCache( void )
{
// For each fragment
int nNumFragments = m_Fragments.TotalCount();
int nNumInvalidFragments = 0;


// Deallocates everything
Warning("Fragments (%d):\n===============\n", nNumFragments);
void DeallocateAllTextures();


// Allocate, deallocate texture
for ( int f = 0; f < nNumFragments; f++ )
TextureHandle_t AllocateTexture( int w, int h );
{
void DeallocateTexture( TextureHandle_t h );
if ( ( m_Fragments[f].m_FrameUsed != 0 ) && ( m_Fragments[f].m_Texture != INVALID_TEXTURE_HANDLE ) )
Warning("Fragment %d, Block: %d, Index: %d, Texture: %d Frame Used: %d\n", f, m_Fragments[f].m_Block, m_Fragments[f].m_Index, m_Fragments[f].m_Texture, m_Fragments[f].m_FrameUsed );
else
nNumInvalidFragments++;
}


// Mark texture as being used... (return true if re-render is needed)
Warning("Invalid Fragments: %d\n", nNumInvalidFragments);
bool UseTexture( TextureHandle_t h, bool bWillRedraw, float flArea );
bool HasValidTexture( TextureHandle_t h );


// Advance frame...
// for ( int c = 0; c <= MAX_TEXTURE_POWER; ++c )
void AdvanceFrame();
// {
// Warning("Cache Index (%d)\n", m_Cache[c].m_List);
// }


// Get at the location of the texture
}
void GetTextureRect(TextureHandle_t handle, int& x, int& y, int& w, int& h );


// Get at the texture it's a part of
//-----------------------------------------------------------------------------
ITexture *GetTexture();
// Adds a block worth of fragments to the LRU
//-----------------------------------------------------------------------------
// Get at the total texture size.
void CTextureAllocator::AddBlockToLRU( int block )
void GetTotalTextureSize( int& w, int& h );
{
int power = m_Blocks[block].m_FragmentPower;
int size = (1 << power);


void DebugPrintCache( void );
// Compute the number of fragments in this block
int fragmentCount = MAX_TEXTURE_SIZE / size;
fragmentCount *= fragmentCount;


private:
// For each fragment, indicate which block it's a part of (and the index)
typedef unsigned short FragmentHandle_t;
// and then stick in at the top of the LRU
while (--fragmentCount >= 0 )
{
FragmentHandle_t f = m_Fragments.Alloc( );
m_Fragments[f].m_Block = block;
m_Fragments[f].m_Index = fragmentCount;
m_Fragments[f].m_Texture = INVALID_TEXTURE_HANDLE;
m_Fragments[f].m_FrameUsed = 0xFFFFFFFF;
m_Fragments.LinkToHead( m_Cache[power].m_List, f );
}
}


enum
//-----------------------------------------------------------------------------
{
// Unlink fragment from cache
INVALID_FRAGMENT_HANDLE = (FragmentHandle_t)~0,
//-----------------------------------------------------------------------------
TEXTURE_PAGE_SIZE     = 1024,
void CTextureAllocator::UnlinkFragmentFromCache( Cache_t& cache, FragmentHandle_t fragment )
MAX_TEXTURE_POWER    = 8,
{
#if !defined( _X360 )
m_Fragments.Unlink( cache.m_List, fragment);
MIN_TEXTURE_POWER     = 4,
}
#else
MIN_TEXTURE_POWER     = 5, // per resolve requirements to ensure 32x32 aligned offsets
#endif
MAX_TEXTURE_SIZE     = (1 << MAX_TEXTURE_POWER),
MIN_TEXTURE_SIZE     = (1 << MIN_TEXTURE_POWER),
BLOCK_SIZE     = MAX_TEXTURE_SIZE,
BLOCKS_PER_ROW     = (TEXTURE_PAGE_SIZE / MAX_TEXTURE_SIZE),
BLOCK_COUNT     = (BLOCKS_PER_ROW * BLOCKS_PER_ROW),
};


struct TextureInfo_t
//-----------------------------------------------------------------------------
{
// Mark something as being used (MRU)..
FragmentHandle_t m_Fragment;
//-----------------------------------------------------------------------------
unsigned short m_Size;
void CTextureAllocator::MarkUsed( FragmentHandle_t fragment )
unsigned short m_Power;
{
};
int block = m_Fragments[fragment].m_Block;
int power = m_Blocks[block].m_FragmentPower;


struct FragmentInfo_t
// Hook it at the end of the LRU
{
Cache_t& cache = m_Cache[power];
unsigned short m_Block;
m_Fragments.LinkToTail( cache.m_List, fragment );
unsigned short m_Index;
m_Fragments[fragment].m_FrameUsed = m_CurrentFrame;
TextureHandle_t m_Texture;
}


// Makes sure we don't overflow
//-----------------------------------------------------------------------------
unsigned int m_FrameUsed;
// Mark something as being unused (LRU)..
};
//-----------------------------------------------------------------------------
void CTextureAllocator::MarkUnused( FragmentHandle_t fragment )
{
int block = m_Fragments[fragment].m_Block;
int power = m_Blocks[block].m_FragmentPower;


struct BlockInfo_t
// Hook it at the end of the LRU
{
Cache_t& cache = m_Cache[power];
unsigned short m_FragmentPower;
m_Fragments.LinkToHead( cache.m_List, fragment );
};
}


struct Cache_t
//-----------------------------------------------------------------------------
{
// Allocate, deallocate texture
unsigned short m_List;
//-----------------------------------------------------------------------------
};
TextureHandle_t CTextureAllocator::AllocateTexture( int w, int h )
{
// Implementational detail for now
Assert( w == h );


// Adds a block worth of fragments to the LRU
// Clamp texture size
void AddBlockToLRU( int block );
if (w < MIN_TEXTURE_SIZE)
w = MIN_TEXTURE_SIZE;
else if (w > MAX_TEXTURE_SIZE)
w = MAX_TEXTURE_SIZE;


// Unlink fragment from cache
TextureHandle_t handle = m_Textures.AddToTail();
void UnlinkFragmentFromCache( Cache_t& cache, FragmentHandle_t fragment );
m_Textures[handle].m_Fragment = INVALID_FRAGMENT_HANDLE;
m_Textures[handle].m_Size = w;


// Mark something as being used (MRU)..
// Find the power of two
void MarkUsed( FragmentHandle_t fragment );
int power = 0;
int size = 1;
while(size < w)
{
size <<= 1;
++power;
}
Assert( size == w );


// Mark something as being unused (LRU)..
m_Textures[handle].m_Power = power;
void MarkUnused( FragmentHandle_t fragment );
 
// Disconnect texture from fragment
void DisconnectTextureFromFragment( FragmentHandle_t f );
 
// Returns the size of a particular fragment
int GetFragmentPower( FragmentHandle_t f ) const;
 
// Stores the actual texture we're writing into
CTextureReference m_TexturePage;
 
CUtlLinkedList< TextureInfo_t, TextureHandle_t > m_Textures;
CUtlMultiList< FragmentInfo_t, FragmentHandle_t > m_Fragments;
 
Cache_t m_Cache[MAX_TEXTURE_POWER+1];
BlockInfo_t m_Blocks[BLOCK_COUNT];
unsigned int m_CurrentFrame;
};


return handle;
}


void CTextureAllocator::DeallocateTexture( TextureHandle_t h )
{
// Warning("Beginning of DeallocateTexture\n");
// DebugPrintCache();


if (m_Textures[h].m_Fragment != INVALID_FRAGMENT_HANDLE)
{
MarkUnused(m_Textures[h].m_Fragment);
m_Fragments[m_Textures[h].m_Fragment].m_FrameUsed = 0xFFFFFFFF; // non-zero frame
DisconnectTextureFromFragment( m_Textures[h].m_Fragment );
}
m_Textures.Remove(h);


// Warning("End of DeallocateTexture\n");
// DebugPrintCache();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Allocate/deallocate the texture page
// Disconnect texture from fragment
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CTextureAllocator::Init()
void CTextureAllocator::DisconnectTextureFromFragment( FragmentHandle_t f )
{
{
for ( int i = 0; i <= MAX_TEXTURE_POWER; ++i )
// Warning( "Beginning of DisconnectTextureFromFragment\n" );
// DebugPrintCache();
 
FragmentInfo_t& info = m_Fragments[f];
if (info.m_Texture != INVALID_TEXTURE_HANDLE)
{
{
m_Cache[i].m_List = m_Fragments.InvalidIndex();
m_Textures[info.m_Texture].m_Fragment = INVALID_FRAGMENT_HANDLE;
}
info.m_Texture = INVALID_TEXTURE_HANDLE;
}


#if !defined( _X360 )
// Warning( "End of DisconnectTextureFromFragment\n" );
// don't need depth buffer for shadows
// DebugPrintCache();
m_TexturePage.InitRenderTarget( TEXTURE_PAGE_SIZE, TEXTURE_PAGE_SIZE, RT_SIZE_NO_CHANGE, IMAGE_FORMAT_ARGB8888, MATERIAL_RT_DEPTH_NONE, false, "_rt_Shadows" );
#else
// unfortunate explicit management required for this render target
// 32bpp edram is only largest shadow fragment, but resolved to actual shadow atlas
// because full-res 1024x1024 shadow buffer is too large for EDRAM
m_TexturePage.InitRenderTargetTexture( TEXTURE_PAGE_SIZE, TEXTURE_PAGE_SIZE, RT_SIZE_NO_CHANGE, IMAGE_FORMAT_ARGB8888, MATERIAL_RT_DEPTH_NONE, false, "_rt_Shadows" );
 
// edram footprint is only 256x256x4 = 256K
m_TexturePage.InitRenderTargetSurface( MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, IMAGE_FORMAT_ARGB8888, false );
 
// due to texture/surface size mismatch, ensure texture page is entirely cleared translucent
// otherwise border artifacts at edge of shadows due to pixel shader averaging of unwanted bits
m_TexturePage->ClearTexture( 0, 0, 0, 0 );
#endif
}
}


void CTextureAllocator::Shutdown()
//-----------------------------------------------------------------------------
// Do we have a valid texture assigned?
//-----------------------------------------------------------------------------
bool CTextureAllocator::HasValidTexture( TextureHandle_t h )
{
{
m_TexturePage.Shutdown();
TextureInfo_t& info = m_Textures[h];
FragmentHandle_t currentFragment = info.m_Fragment;
return (currentFragment != INVALID_FRAGMENT_HANDLE);
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Initialize the allocator with something that knows how to refresh the bits
// Mark texture as being used...
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CTextureAllocator::Reset()
bool CTextureAllocator::UseTexture( TextureHandle_t h, bool bWillRedraw, float flArea )
{
{
DeallocateAllTextures();
// Warning( "Top of UseTexture\n" );
// DebugPrintCache();


m_Textures.EnsureCapacity(256);
TextureInfo_t& info = m_Textures[h];
m_Fragments.EnsureCapacity(256);


// Set up the block sizes....
// spin up to the best fragment size
// FIXME: Improve heuristic?!?
int nDesiredPower = MIN_TEXTURE_POWER;
#if !defined( _X360 )
int nDesiredWidth = MIN_TEXTURE_SIZE;
m_Blocks[0].m_FragmentPower  = MAX_TEXTURE_POWER-4; // 128 cells at ExE resolution
while ( (nDesiredWidth * nDesiredWidth) < flArea )
#else
m_Blocks[0].m_FragmentPower  = MAX_TEXTURE_POWER-3; // 64 cells at DxD resolution
#endif
m_Blocks[1].m_FragmentPower  = MAX_TEXTURE_POWER-3; // 64 cells at DxD resolution
m_Blocks[2].m_FragmentPower  = MAX_TEXTURE_POWER-2; // 32 cells at CxC resolution
m_Blocks[3].m_FragmentPower  = MAX_TEXTURE_POWER-2;
m_Blocks[4].m_FragmentPower  = MAX_TEXTURE_POWER-1; // 24 cells at BxB resolution
m_Blocks[5].m_FragmentPower  = MAX_TEXTURE_POWER-1;
m_Blocks[6].m_FragmentPower  = MAX_TEXTURE_POWER-1;
m_Blocks[7].m_FragmentPower  = MAX_TEXTURE_POWER-1;
m_Blocks[8].m_FragmentPower  = MAX_TEXTURE_POWER-1;
m_Blocks[9].m_FragmentPower  = MAX_TEXTURE_POWER-1;
m_Blocks[10].m_FragmentPower = MAX_TEXTURE_POWER; // 6 cells at AxA resolution
m_Blocks[11].m_FragmentPower = MAX_TEXTURE_POWER;
m_Blocks[12].m_FragmentPower = MAX_TEXTURE_POWER;
m_Blocks[13].m_FragmentPower = MAX_TEXTURE_POWER;
m_Blocks[14].m_FragmentPower = MAX_TEXTURE_POWER;
m_Blocks[15].m_FragmentPower = MAX_TEXTURE_POWER;
 
// Initialize the LRU
int i;
for ( i = 0; i <= MAX_TEXTURE_POWER; ++i )
{
{
m_Cache[i].m_List = m_Fragments.CreateList();
if ( nDesiredPower >= info.m_Power )
}
{
nDesiredPower = info.m_Power;
break;
}


// Now that the block sizes are allocated, create LRUs for the various block sizes
++nDesiredPower;
for ( i = 0; i < BLOCK_COUNT; ++i)
nDesiredWidth <<= 1;
{
// Initialize LRU
AddBlockToLRU( i );
}
}


m_CurrentFrame = 0;
// If we've got a valid fragment for this texture, no worries!
}
int nCurrentPower = -1;
 
FragmentHandle_t currentFragment = info.m_Fragment;
void CTextureAllocator::DeallocateAllTextures()
if (currentFragment != INVALID_FRAGMENT_HANDLE)
{
m_Textures.Purge();
m_Fragments.Purge();
for ( int i = 0; i <= MAX_TEXTURE_POWER; ++i )
{
{
m_Cache[i].m_List = m_Fragments.InvalidIndex();
// If the current fragment is at or near the desired power, we're done
nCurrentPower = GetFragmentPower(info.m_Fragment);
Assert( nCurrentPower <= info.m_Power );
bool bShouldKeepTexture = (!bWillRedraw) && (nDesiredPower < 8) && (nDesiredPower - nCurrentPower <= 1);
if ((nCurrentPower == nDesiredPower) || bShouldKeepTexture)
{
// Move to the back of the LRU
MarkUsed( currentFragment );
return false;
}
}
}
}


// Warning( "\n\nUseTexture B\n" );
// DebugPrintCache();


//-----------------------------------------------------------------------------
// Grab the LRU fragment from the appropriate cache
// Dump the state of the cache to debug out
// If that fragment is connected to a texture, disconnect it.
//-----------------------------------------------------------------------------
int power = nDesiredPower;
void CTextureAllocator::DebugPrintCache( void )
{
// For each fragment
int nNumFragments = m_Fragments.TotalCount();
int nNumInvalidFragments = 0;


Warning("Fragments (%d):\n===============\n", nNumFragments);
FragmentHandle_t f = INVALID_FRAGMENT_HANDLE;
 
bool done = false;
for ( int f = 0; f < nNumFragments; f++ )
while (!done && power >= 0)
{
{
if ( ( m_Fragments[f].m_FrameUsed != 0 ) && ( m_Fragments[f].m_Texture != INVALID_TEXTURE_HANDLE ) )
f = m_Fragments.Head( m_Cache[power].m_List );
Warning("Fragment %d, Block: %d, Index: %d, Texture: %d Frame Used: %d\n", f, m_Fragments[f].m_Block, m_Fragments[f].m_Index, m_Fragments[f].m_Texture, m_Fragments[f].m_FrameUsed );
// This represents an overflow condition (used too many textures of
// the same size in a single frame). It that happens, just use a texture
// of lower res.
if ( (f != m_Fragments.InvalidIndex()) && (m_Fragments[f].m_FrameUsed != m_CurrentFrame) )
{
done = true;
}
else
else
nNumInvalidFragments++;
{
--power;
}
}
}


Warning("Invalid Fragments: %d\n", nNumInvalidFragments);
// Warning( "\n\nUseTexture C\n" );
// DebugPrintCache();


// for ( int c = 0; c <= MAX_TEXTURE_POWER; ++c )
// Ok, lets see if we're better off than we were...
// {
if (currentFragment != INVALID_FRAGMENT_HANDLE)
// Warning("Cache Index (%d)\n", m_Cache[c].m_List);
{
// }
if (power <= nCurrentPower)
{
// Oops... we're not. Let's leave well enough alone
// Move to the back of the LRU
MarkUsed( currentFragment );
return false;
}
else
{
// Clear out the old fragment
DisconnectTextureFromFragment(currentFragment);
}
}


}
if ( f == INVALID_FRAGMENT_HANDLE )
{
return false;
}


// Disconnect existing texture from this fragment (if necessary)
DisconnectTextureFromFragment(f);


//-----------------------------------------------------------------------------
// Connnect new texture to this fragment
// Adds a block worth of fragments to the LRU
info.m_Fragment = f;
//-----------------------------------------------------------------------------
m_Fragments[f].m_Texture = h;
void CTextureAllocator::AddBlockToLRU( int block )
{
int power = m_Blocks[block].m_FragmentPower;
int size = (1 << power);


// Compute the number of fragments in this block
// Move to the back of the LRU
int fragmentCount = MAX_TEXTURE_SIZE / size;
MarkUsed( f );
fragmentCount *= fragmentCount;


// For each fragment, indicate which block it's a part of (and the index)
// Indicate we need a redraw
// and then stick in at the top of the LRU
return true;
while (--fragmentCount >= 0 )
{
FragmentHandle_t f = m_Fragments.Alloc( );
m_Fragments[f].m_Block = block;
m_Fragments[f].m_Index = fragmentCount;
m_Fragments[f].m_Texture = INVALID_TEXTURE_HANDLE;
m_Fragments[f].m_FrameUsed = 0xFFFFFFFF;
m_Fragments.LinkToHead( m_Cache[power].m_List, f );
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Unlink fragment from cache
// Returns the size of a particular fragment
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CTextureAllocator::UnlinkFragmentFromCache( Cache_t& cache, FragmentHandle_t fragment )
int CTextureAllocator::GetFragmentPower( FragmentHandle_t f ) const
{
{
m_Fragments.Unlink( cache.m_List, fragment);
return m_Blocks[m_Fragments[f].m_Block].m_FragmentPower;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Mark something as being used (MRU)..
// Advance frame...
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CTextureAllocator::MarkUsed( FragmentHandle_t fragment )
void CTextureAllocator::AdvanceFrame()
{
{
int block = m_Fragments[fragment].m_Block;
// Be sure that this is called as infrequently as possible (i.e. once per frame,
int power = m_Blocks[block].m_FragmentPower;
// NOT once per view) to prevent cache thrash when rendering multiple views in a single frame
m_CurrentFrame++;
}


// Hook it at the end of the LRU
//-----------------------------------------------------------------------------
Cache_t& cache = m_Cache[power];
// Prepare to render into texture...
m_Fragments.LinkToTail( cache.m_List, fragment );
//-----------------------------------------------------------------------------
m_Fragments[fragment].m_FrameUsed = m_CurrentFrame;
ITexture* CTextureAllocator::GetTexture()
{
return m_TexturePage;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Mark something as being unused (LRU)..
// Get at the total texture size.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CTextureAllocator::MarkUnused( FragmentHandle_t fragment )
void CTextureAllocator::GetTotalTextureSize( int& w, int& h )
{
{
int block = m_Fragments[fragment].m_Block;
w = h = TEXTURE_PAGE_SIZE;
int power = m_Blocks[block].m_FragmentPower;
 
// Hook it at the end of the LRU
Cache_t& cache = m_Cache[power];
m_Fragments.LinkToHead( cache.m_List, fragment );
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Allocate, deallocate texture
// Returns the rectangle the texture lives in..
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
TextureHandle_t CTextureAllocator::AllocateTexture( int w, int h )
void CTextureAllocator::GetTextureRect(TextureHandle_t handle, int& x, int& y, int& w, int& h )
{
{
// Implementational detail for now
TextureInfo_t& info = m_Textures[handle];
Assert( w == h );
Assert( info.m_Fragment != INVALID_FRAGMENT_HANDLE );


// Clamp texture size
// Compute the position of the fragment in the page
if (w < MIN_TEXTURE_SIZE)
FragmentInfo_t& fragment = m_Fragments[info.m_Fragment];
w = MIN_TEXTURE_SIZE;
int blockY = fragment.m_Block / BLOCKS_PER_ROW;
else if (w > MAX_TEXTURE_SIZE)
int blockX = fragment.m_Block - blockY * BLOCKS_PER_ROW;
w = MAX_TEXTURE_SIZE;


TextureHandle_t handle = m_Textures.AddToTail();
int fragmentSize = (1 << m_Blocks[fragment.m_Block].m_FragmentPower);
m_Textures[handle].m_Fragment = INVALID_FRAGMENT_HANDLE;
int fragmentsPerRow = BLOCK_SIZE / fragmentSize;
m_Textures[handle].m_Size = w;
int fragmentY = fragment.m_Index / fragmentsPerRow;
int fragmentX = fragment.m_Index - fragmentY * fragmentsPerRow;


// Find the power of two
x = blockX * BLOCK_SIZE + fragmentX * fragmentSize;
int power = 0;
y = blockY * BLOCK_SIZE + fragmentY * fragmentSize;
int size = 1;
w = fragmentSize;
while(size < w)
h = fragmentSize;
{
}
size <<= 1;
++power;
}
Assert( size == w );


m_Textures[handle].m_Power = power;
//-----------------------------------------------------------------------------
// Defines how big of a shadow texture we should be making per caster...
//-----------------------------------------------------------------------------
#define TEXEL_SIZE_PER_CASTER_SIZE 2.0f
#define MAX_FALLOFF_AMOUNT 240
#define MAX_CLIP_PLANE_COUNT 4
#define SHADOW_CULL_TOLERANCE 0.5f


return handle;
static ConVar r_shadows( "r_shadows", "1" ); // hook into engine's cvars..
}
static ConVar r_shadowmaxrendered("r_shadowmaxrendered", "32");
static ConVar r_shadows_gamecontrol( "r_shadows_gamecontrol", "-1", FCVAR_CHEAT ); // hook into engine's cvars..


void CTextureAllocator::DeallocateTexture( TextureHandle_t h )
//-----------------------------------------------------------------------------
// The class responsible for dealing with shadows on the client side
// Oh, and let's take a moment and notice how happy Robin and John must be
// owing to the lack of space between this lovely comment and the class name =)
//-----------------------------------------------------------------------------
class CClientShadowMgr : public IClientShadowMgr
{
{
// Warning("Beginning of DeallocateTexture\n");
public:
// DebugPrintCache();
CClientShadowMgr();
 
virtual char const *Name() { return "CCLientShadowMgr"; }


if (m_Textures[h].m_Fragment != INVALID_FRAGMENT_HANDLE)
// Inherited from IClientShadowMgr
{
virtual bool Init();
MarkUnused(m_Textures[h].m_Fragment);
virtual void PostInit() {}
m_Fragments[m_Textures[h].m_Fragment].m_FrameUsed = 0xFFFFFFFF; // non-zero frame
virtual void Shutdown();
DisconnectTextureFromFragment( m_Textures[h].m_Fragment );
virtual void LevelInitPreEntity();
}
virtual void LevelInitPostEntity() {}
m_Textures.Remove(h);
virtual void LevelShutdownPreEntity() {}
virtual void LevelShutdownPostEntity();


// Warning("End of DeallocateTexture\n");
virtual bool IsPerFrame() { return true; }
// DebugPrintCache();
}


virtual void PreRender();
virtual void Update( float frametime ) { }
virtual void PostRender() {}


//-----------------------------------------------------------------------------
virtual void OnSave() {}
// Disconnect texture from fragment
virtual void OnRestore() {}
//-----------------------------------------------------------------------------
virtual void SafeRemoveIfDesired() {}
void CTextureAllocator::DisconnectTextureFromFragment( FragmentHandle_t f )
{
// Warning( "Beginning of DisconnectTextureFromFragment\n" );
// DebugPrintCache();


FragmentInfo_t& info = m_Fragments[f];
virtual ClientShadowHandle_t CreateShadow( ClientEntityHandle_t entity, int flags );
if (info.m_Texture != INVALID_TEXTURE_HANDLE)
virtual void DestroyShadow( ClientShadowHandle_t handle );
{
m_Textures[info.m_Texture].m_Fragment = INVALID_FRAGMENT_HANDLE;
info.m_Texture = INVALID_TEXTURE_HANDLE;
}


// Create flashlight (projected texture light source)
virtual ClientShadowHandle_t CreateFlashlight( const FlashlightState_t &lightState );
virtual void UpdateFlashlightState( ClientShadowHandle_t shadowHandle, const FlashlightState_t &lightState );
virtual void DestroyFlashlight( ClientShadowHandle_t shadowHandle );


// Warning( "End of DisconnectTextureFromFragment\n" );
// Update a shadow
// DebugPrintCache();
virtual void UpdateProjectedTexture( ClientShadowHandle_t handle, bool force );
}


void ComputeBoundingSphere( IClientRenderable* pRenderable, Vector& origin, float& radius );


//-----------------------------------------------------------------------------
virtual void AddToDirtyShadowList( ClientShadowHandle_t handle, bool bForce );
// Do we have a valid texture assigned?
virtual void AddToDirtyShadowList( IClientRenderable *pRenderable, bool force );
//-----------------------------------------------------------------------------
bool CTextureAllocator::HasValidTexture( TextureHandle_t h )
{
TextureInfo_t& info = m_Textures[h];
FragmentHandle_t currentFragment = info.m_Fragment;
return (currentFragment != INVALID_FRAGMENT_HANDLE);
}


// Marks the render-to-texture shadow as needing to be re-rendered
virtual void MarkRenderToTextureShadowDirty( ClientShadowHandle_t handle );


//-----------------------------------------------------------------------------
// deals with shadows being added to shadow receivers
// Mark texture as being used...
void AddShadowToReceiver( ClientShadowHandle_t handle,
//-----------------------------------------------------------------------------
IClientRenderable* pRenderable, ShadowReceiver_t type );
bool CTextureAllocator::UseTexture( TextureHandle_t h, bool bWillRedraw, float flArea )
{
// Warning( "Top of UseTexture\n" );
// DebugPrintCache();


TextureInfo_t& info = m_Textures[h];
// deals with shadows being added to shadow receivers
void RemoveAllShadowsFromReceiver( IClientRenderable* pRenderable, ShadowReceiver_t type );


// spin up to the best fragment size
// Re-renders all shadow textures for shadow casters that lie in the leaf list
int nDesiredPower = MIN_TEXTURE_POWER;
void ComputeShadowTextures( const CViewSetup &view, int leafCount, LeafIndex_t* pLeafList );
int nDesiredWidth = MIN_TEXTURE_SIZE;
while ( (nDesiredWidth * nDesiredWidth) < flArea )
{
if ( nDesiredPower >= info.m_Power )
{
nDesiredPower = info.m_Power;
break;
}


++nDesiredPower;
// Kicks off rendering into shadow depth maps (if any)
nDesiredWidth <<= 1;
void ComputeShadowDepthTextures( const CViewSetup &view );
}
 
// Frees shadow depth textures for use in subsequent view/frame
void FreeShadowDepthTextures();
 
// Returns the shadow texture
ITexture* GetShadowTexture( unsigned short h );


// If we've got a valid fragment for this texture, no worries!
// Returns shadow information
int nCurrentPower = -1;
const ShadowInfo_t& GetShadowInfo( ClientShadowHandle_t h );
FragmentHandle_t currentFragment = info.m_Fragment;
if (currentFragment != INVALID_FRAGMENT_HANDLE)
{
// If the current fragment is at or near the desired power, we're done
nCurrentPower = GetFragmentPower(info.m_Fragment);
Assert( nCurrentPower <= info.m_Power );
bool bShouldKeepTexture = (!bWillRedraw) && (nDesiredPower < 8) && (nDesiredPower - nCurrentPower <= 1);
if ((nCurrentPower == nDesiredPower) || bShouldKeepTexture)
{
// Move to the back of the LRU
MarkUsed( currentFragment );
return false;
}
}


// Warning( "\n\nUseTexture B\n" );
// Renders the shadow texture to screen...
// DebugPrintCache();
void RenderShadowTexture( int w, int h );


// Grab the LRU fragment from the appropriate cache
// Sets the shadow direction
// If that fragment is connected to a texture, disconnect it.
virtual void SetShadowDirection( const Vector& dir );
int power = nDesiredPower;
const Vector &GetShadowDirection() const;


FragmentHandle_t f = INVALID_FRAGMENT_HANDLE;
// Sets the shadow color
bool done = false;
virtual void SetShadowColor( unsigned char r, unsigned char g, unsigned char b );
while (!done && power >= 0)
void GetShadowColor( unsigned char *r, unsigned char *g, unsigned char *b ) const;
{
 
f = m_Fragments.Head( m_Cache[power].m_List );
// Sets the shadow distance
virtual void SetShadowDistance( float flMaxDistance );
// This represents an overflow condition (used too many textures of
float GetShadowDistance( ) const;
// the same size in a single frame). It that happens, just use a texture
// of lower res.
if ( (f != m_Fragments.InvalidIndex()) && (m_Fragments[f].m_FrameUsed != m_CurrentFrame) )
{
done = true;
}
else
{
--power;
}
}


// Sets the screen area at which blobby shadows are always used
virtual void SetShadowBlobbyCutoffArea( float flMinArea );
float GetBlobbyCutoffArea( ) const;


// Warning( "\n\nUseTexture C\n" );
// Set the darkness falloff bias
// DebugPrintCache();
virtual void SetFalloffBias( ClientShadowHandle_t handle, unsigned char ucBias );


// Ok, lets see if we're better off than we were...
void RestoreRenderState();
if (currentFragment != INVALID_FRAGMENT_HANDLE)
{
if (power <= nCurrentPower)
{
// Oops... we're not. Let's leave well enough alone
// Move to the back of the LRU
MarkUsed( currentFragment );
return false;
}
else
{
// Clear out the old fragment
DisconnectTextureFromFragment(currentFragment);
}
}


if ( f == INVALID_FRAGMENT_HANDLE )
// Computes a rough bounding box encompassing the volume of the shadow
{
    void ComputeShadowBBox( IClientRenderable *pRenderable, ClientShadowHandle_t shadowHandle, const Vector &vecAbsCenter, float flRadius, Vector *pAbsMins, Vector *pAbsMaxs );
return false;
}


// Disconnect existing texture from this fragment (if necessary)
bool WillParentRenderBlobbyShadow( IClientRenderable *pRenderable );
DisconnectTextureFromFragment(f);


// Connnect new texture to this fragment
// Are we the child of a shadow with render-to-texture?
info.m_Fragment = f;
bool ShouldUseParentShadow( IClientRenderable *pRenderable );
m_Fragments[f].m_Texture = h;


// Move to the back of the LRU
void SetShadowsDisabled( bool bDisabled )
MarkUsed( f );
{
r_shadows_gamecontrol.SetValue( bDisabled != 1 );
}


// Indicate we need a redraw
void SuppressShadowFromWorldLights( bool bSuppress );
return true;
    void SetShadowFromWorldLightsEnabled( bool bEnabled );
}
    bool IsShadowingFromWorldLights() const { return m_bShadowFromWorldLights; }


private:
enum
{
SHADOW_FLAGS_TEXTURE_DIRTY = (CLIENT_SHADOW_FLAGS_LAST_FLAG << 1),
SHADOW_FLAGS_BRUSH_MODEL = (CLIENT_SHADOW_FLAGS_LAST_FLAG << 2),
SHADOW_FLAGS_USING_LOD_SHADOW = (CLIENT_SHADOW_FLAGS_LAST_FLAG << 3),
SHADOW_FLAGS_LIGHT_WORLD = (CLIENT_SHADOW_FLAGS_LAST_FLAG << 4),
};


//-----------------------------------------------------------------------------
struct ClientShadow_t
// Returns the size of a particular fragment
{
//-----------------------------------------------------------------------------
ClientEntityHandle_t m_Entity;
int CTextureAllocator::GetFragmentPower( FragmentHandle_t f ) const
ShadowHandle_t m_ShadowHandle;
{
ClientLeafShadowHandle_t m_ClientLeafShadowHandle;
return m_Blocks[m_Fragments[f].m_Block].m_FragmentPower;
unsigned short m_Flags;
}
VMatrix m_WorldToShadow;
Vector2D m_WorldSize;
Vector m_ShadowDir;
Vector m_LastOrigin;
QAngle m_LastAngles;
Vector m_CurrentLightPos; // When shadowing from local lights, stores the position of the currently shadowing light
        Vector m_TargetLightPos; // When shadowing from local lights, stores the position of the new shadowing light
        float m_LightPosLerp; // Lerp progress when going from current to target light
TextureHandle_t m_ShadowTexture;
CTextureReference m_ShadowDepthTexture;
int m_nRenderFrame;
EHANDLE m_hTargetEntity;
};


private:
// Shadow update functions
void UpdateStudioShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle );
void UpdateBrushShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle );
void UpdateShadow( ClientShadowHandle_t handle, bool force );


//-----------------------------------------------------------------------------
// Gets the entity whose shadow this shadow will render into
// Advance frame...
IClientRenderable *GetParentShadowEntity( ClientShadowHandle_t handle );
//-----------------------------------------------------------------------------
void CTextureAllocator::AdvanceFrame()
{
// Be sure that this is called as infrequently as possible (i.e. once per frame,
// NOT once per view) to prevent cache thrash when rendering multiple views in a single frame
m_CurrentFrame++;
}


// Adds the child bounds to the bounding box
void AddChildBounds( matrix3x4_t &matWorldToBBox, IClientRenderable* pParent, Vector &vecMins, Vector &vecMaxs );


//-----------------------------------------------------------------------------
// Compute a bounds for the entity + children
// Prepare to render into texture...
void ComputeHierarchicalBounds( IClientRenderable *pRenderable, Vector &vecMins, Vector &vecMaxs );
//-----------------------------------------------------------------------------
ITexture* CTextureAllocator::GetTexture()
{
return m_TexturePage;
}


//-----------------------------------------------------------------------------
// Builds matrices transforming from world space to shadow space
// Get at the total texture size.
void BuildGeneralWorldToShadowMatrix( VMatrix& matWorldToShadow,
//-----------------------------------------------------------------------------
const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec );
void CTextureAllocator::GetTotalTextureSize( int& w, int& h )
 
{
void BuildWorldToShadowMatrix( VMatrix& matWorldToShadow, const Vector& origin, const Quaternion& quatOrientation );
w = h = TEXTURE_PAGE_SIZE;
}


void BuildPerspectiveWorldToFlashlightMatrix( VMatrix& matWorldToShadow, const FlashlightState_t &flashlightState );


//-----------------------------------------------------------------------------
// Update a shadow
// Returns the rectangle the texture lives in..
void UpdateProjectedTextureInternal( ClientShadowHandle_t handle, bool force );
//-----------------------------------------------------------------------------
void CTextureAllocator::GetTextureRect(TextureHandle_t handle, int& x, int& y, int& w, int& h )
{
TextureInfo_t& info = m_Textures[handle];
Assert( info.m_Fragment != INVALID_FRAGMENT_HANDLE );


// Compute the position of the fragment in the page
// Compute the shadow origin and attenuation start distance
FragmentInfo_t& fragment = m_Fragments[info.m_Fragment];
float ComputeLocalShadowOrigin( IClientRenderable* pRenderable,
int blockY = fragment.m_Block / BLOCKS_PER_ROW;
const Vector& mins, const Vector& maxs, const Vector& localShadowDir, float backupFactor, Vector& origin );
int blockX = fragment.m_Block - blockY * BLOCKS_PER_ROW;


int fragmentSize = (1 << m_Blocks[fragment.m_Block].m_FragmentPower);
// Remove a shadow from the dirty list
int fragmentsPerRow = BLOCK_SIZE / fragmentSize;
void RemoveShadowFromDirtyList( ClientShadowHandle_t handle );
int fragmentY = fragment.m_Index / fragmentsPerRow;
int fragmentX = fragment.m_Index - fragmentY * fragmentsPerRow;


x = blockX * BLOCK_SIZE + fragmentX * fragmentSize;
// NOTE: this will ONLY return SHADOWS_NONE, SHADOWS_SIMPLE, or SHADOW_RENDER_TO_TEXTURE.
y = blockY * BLOCK_SIZE + fragmentY * fragmentSize;
ShadowType_t GetActualShadowCastType( ClientShadowHandle_t handle ) const;
w = fragmentSize;
ShadowType_t GetActualShadowCastType( IClientRenderable *pRenderable ) const;
h = fragmentSize;
}


// Builds a simple blobby shadow
void BuildOrthoShadow( IClientRenderable* pRenderable, ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs);


//-----------------------------------------------------------------------------
// Builds a more complex shadow...
// Defines how big of a shadow texture we should be making per caster...
void BuildRenderToTextureShadow( IClientRenderable* pRenderable,
//-----------------------------------------------------------------------------
ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs );
#define TEXEL_SIZE_PER_CASTER_SIZE 2.0f
#define MAX_FALLOFF_AMOUNT 240
#define MAX_CLIP_PLANE_COUNT 4
#define SHADOW_CULL_TOLERANCE 0.5f


static ConVar r_shadows( "r_shadows", "1" ); // hook into engine's cvars..
// Build a projected-texture flashlight
static ConVar r_shadowmaxrendered("r_shadowmaxrendered", "32");
void BuildFlashlight( ClientShadowHandle_t handle );
static ConVar r_shadows_gamecontrol( "r_shadows_gamecontrol", "-1", FCVAR_CHEAT ); // hook into engine's cvars..


//-----------------------------------------------------------------------------
// Does all the lovely stuff we need to do to have render-to-texture shadows
// The class responsible for dealing with shadows on the client side
void SetupRenderToTextureShadow( ClientShadowHandle_t h );
// Oh, and let's take a moment and notice how happy Robin and John must be
void CleanUpRenderToTextureShadow( ClientShadowHandle_t h );
// owing to the lack of space between this lovely comment and the class name =)
//-----------------------------------------------------------------------------
class CClientShadowMgr : public IClientShadowMgr
{
public:
CClientShadowMgr();


virtual char const *Name() { return "CCLientShadowMgr"; }
// Compute the extra shadow planes
void ComputeExtraClipPlanes( IClientRenderable* pRenderable,
ClientShadowHandle_t handle, const Vector* vec,
const Vector& mins, const Vector& maxs, const Vector& localShadowDir );


// Inherited from IClientShadowMgr
// Set extra clip planes related to shadows...
virtual bool Init();
void ClearExtraClipPlanes( ClientShadowHandle_t h );
virtual void PostInit() {}
void AddExtraClipPlane( ClientShadowHandle_t h, const Vector& normal, float dist );
virtual void Shutdown();
virtual void LevelInitPreEntity();
virtual void LevelInitPostEntity() {}
virtual void LevelShutdownPreEntity() {}
virtual void LevelShutdownPostEntity();


virtual bool IsPerFrame() { return true; }
// Cull if the origin is on the wrong side of a shadow clip plane....
bool CullReceiver( ClientShadowHandle_t handle, IClientRenderable* pRenderable, IClientRenderable* pSourceRenderable );


virtual void PreRender();
bool ComputeSeparatingPlane( IClientRenderable* pRend1, IClientRenderable* pRend2, cplane_t* pPlane );
virtual void Update( float frametime ) { }
virtual void PostRender() {}


virtual void OnSave() {}
// Causes all shadows to be re-updated
virtual void OnRestore() {}
void UpdateAllShadows();
virtual void SafeRemoveIfDesired() {}


virtual ClientShadowHandle_t CreateShadow( ClientEntityHandle_t entity, int flags );
// One of these gets called with every shadow that potentially will need to re-render
virtual void DestroyShadow( ClientShadowHandle_t handle );
bool DrawRenderToTextureShadow( unsigned short clientShadowHandle, float flArea );
void DrawRenderToTextureShadowLOD( unsigned short clientShadowHandle );


// Create flashlight (projected texture light source)
// Draws all children shadows into our own
virtual ClientShadowHandle_t CreateFlashlight( const FlashlightState_t &lightState );
bool DrawShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild = false );
virtual void UpdateFlashlightState( ClientShadowHandle_t shadowHandle, const FlashlightState_t &lightState );
virtual void DestroyFlashlight( ClientShadowHandle_t shadowHandle );


// Update a shadow
// Setup stage for threading
virtual void UpdateProjectedTexture( ClientShadowHandle_t handle, bool force );
bool BuildSetupListForRenderToTextureShadow( unsigned short clientShadowHandle, float flArea );
bool BuildSetupShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild = false );


void ComputeBoundingSphere( IClientRenderable* pRenderable, Vector& origin, float& radius );
// Computes + sets the render-to-texture texcoords
void SetRenderToTextureShadowTexCoords( ShadowHandle_t handle, int x, int y, int w, int h );


virtual void AddToDirtyShadowList( ClientShadowHandle_t handle, bool bForce );
// Visualization....
virtual void AddToDirtyShadowList( IClientRenderable *pRenderable, bool force );
void DrawRenderToTextureDebugInfo( IClientRenderable* pRenderable, const Vector& mins, const Vector& maxs );


// Marks the render-to-texture shadow as needing to be re-rendered
// Advance frame
virtual void MarkRenderToTextureShadowDirty( ClientShadowHandle_t handle );
void AdvanceFrame();


// deals with shadows being added to shadow receivers
// Returns renderable-specific shadow info
void AddShadowToReceiver( ClientShadowHandle_t handle,
float GetShadowDistance( IClientRenderable *pRenderable ) const;
IClientRenderable* pRenderable, ShadowReceiver_t type );
const Vector &GetShadowDirection( IClientRenderable *pRenderable ) const;
const Vector &GetShadowDirection( ClientShadowHandle_t shadowHandle ) const;


// deals with shadows being added to shadow receivers
// Initialize, shutdown render-to-texture shadows
void RemoveAllShadowsFromReceiver( IClientRenderable* pRenderable, ShadowReceiver_t type );
void InitDepthTextureShadows();
void ShutdownDepthTextureShadows();


// Re-renders all shadow textures for shadow casters that lie in the leaf list
// Initialize, shutdown render-to-texture shadows
void ComputeShadowTextures( const CViewSetup &view, int leafCount, LeafIndex_t* pLeafList );
void InitRenderToTextureShadows();
void ShutdownRenderToTextureShadows();


// Kicks off rendering into shadow depth maps (if any)
static bool ShadowHandleCompareFunc( const ClientShadowHandle_t& lhs, const ClientShadowHandle_t& rhs )
void ComputeShadowDepthTextures( const CViewSetup &view );
{
return lhs < rhs;
}


// Frees shadow depth textures for use in subsequent view/frame
ClientShadowHandle_t CreateProjectedTexture( ClientEntityHandle_t entity, int flags );
void FreeShadowDepthTextures();


// Returns the shadow texture
// Lock down the usage of a shadow depth texture...must be unlocked use on subsequent views / frames
ITexture* GetShadowTexture( unsigned short h );
bool LockShadowDepthTexture( CTextureReference *shadowDepthTexture );
void UnlockAllShadowDepthTextures();


// Returns shadow information
// Set and clear flashlight target renderable
const ShadowInfo_t& GetShadowInfo( ClientShadowHandle_t h );
void SetFlashlightTarget( ClientShadowHandle_t shadowHandle, EHANDLE targetEntity );


// Renders the shadow texture to screen...
// Set flashlight light world flag
void RenderShadowTexture( int w, int h );
void SetFlashlightLightWorld( ClientShadowHandle_t shadowHandle, bool bLightWorld );


// Sets the shadow direction
bool IsFlashlightTarget( ClientShadowHandle_t shadowHandle, IClientRenderable *pRenderable );
virtual void SetShadowDirection( const Vector& dir );
const Vector &GetShadowDirection() const;


// Sets the shadow color
// Builds a list of active shadows requiring shadow depth renders
virtual void SetShadowColor( unsigned char r, unsigned char g, unsigned char b );
int BuildActiveShadowDepthList( const CViewSetup &viewSetup, int nMaxDepthShadows, ClientShadowHandle_t *pActiveDepthShadows );
void GetShadowColor( unsigned char *r, unsigned char *g, unsigned char *b ) const;


// Sets the shadow distance
// Sets the view's active flashlight render state
virtual void SetShadowDistance( float flMaxDistance );
void SetViewFlashlightState( int nActiveFlashlightCount, ClientShadowHandle_t* pActiveFlashlights );
float GetShadowDistance( ) const;
void UpdateDirtyShadow( ClientShadowHandle_t handle );
    void UpdateShadowDirectionFromLocalLightSource( ClientShadowHandle_t shadowHandle );


// Sets the screen area at which blobby shadows are always used
private:
virtual void SetShadowBlobbyCutoffArea( float flMinArea );
Vector m_SimpleShadowDir;
float GetBlobbyCutoffArea( ) const;
color32 m_AmbientLightColor;
CMaterialReference m_SimpleShadow;
CMaterialReference m_RenderShadow;
CMaterialReference m_RenderModelShadow;
CTextureReference m_DummyColorTexture;
CUtlLinkedList< ClientShadow_t, ClientShadowHandle_t > m_Shadows;
CTextureAllocator m_ShadowAllocator;


// Set the darkness falloff bias
bool m_RenderToTextureActive;
virtual void SetFalloffBias( ClientShadowHandle_t handle, unsigned char ucBias );
bool m_bRenderTargetNeedsClear;
bool m_bUpdatingDirtyShadows;
bool m_bThreaded;
float m_flShadowCastDist;
float m_flMinShadowArea;
CUtlRBTree< ClientShadowHandle_t, unsigned short > m_DirtyShadows;
CUtlVector< ClientShadowHandle_t > m_TransparentShadows;


void RestoreRenderState();
// These members maintain current state of depth texturing (size and global active state)
// If either changes in a frame, PreRender() will catch it and do the appropriate allocation, deallocation or reallocation
bool m_bDepthTextureActive;
int m_nDepthTextureResolution; // Assume square (height == width)
bool m_bShadowFromWorldLights;


// Computes a rough bounding box encompassing the volume of the shadow
CUtlVector< CTextureReference > m_DepthTextureCache;
    void ComputeShadowBBox( IClientRenderable *pRenderable, ClientShadowHandle_t shadowHandle, const Vector &vecAbsCenter, float flRadius, Vector *pAbsMins, Vector *pAbsMaxs );
CUtlVector< bool > m_DepthTextureCacheLocks;
int m_nMaxDepthTextureShadows;


bool WillParentRenderBlobbyShadow( IClientRenderable *pRenderable );
friend class CVisibleShadowList;
friend class CVisibleShadowFrustumList;
};


// Are we the child of a shadow with render-to-texture?
//-----------------------------------------------------------------------------
bool ShouldUseParentShadow( IClientRenderable *pRenderable );
// Singleton
//-----------------------------------------------------------------------------
static CClientShadowMgr s_ClientShadowMgr;
IClientShadowMgr* g_pClientShadowMgr = &s_ClientShadowMgr;
 
//-----------------------------------------------------------------------------
// Builds a list of potential shadows that lie within our PVS + view frustum
//-----------------------------------------------------------------------------
struct VisibleShadowInfo_t
{
ClientShadowHandle_t m_hShadow;
float m_flArea;
Vector m_vecAbsCenter;
};
 
class CVisibleShadowList : public IClientLeafShadowEnum
{
public:


void SetShadowsDisabled( bool bDisabled )  
CVisibleShadowList();
{
int FindShadows( const CViewSetup *pView, int nLeafCount, LeafIndex_t *pLeafList );
r_shadows_gamecontrol.SetValue( bDisabled != 1 );
int GetVisibleShadowCount() const;
}


void SuppressShadowFromWorldLights( bool bSuppress );
const VisibleShadowInfo_t &GetVisibleShadow( int i ) const;
    void SetShadowFromWorldLightsEnabled( bool bEnabled );
    bool IsShadowingFromWorldLights() const { return m_bShadowFromWorldLights; }


private:
private:
enum
void EnumShadow( unsigned short clientShadowHandle );
{
float ComputeScreenArea( const Vector &vecCenter, float r ) const;
SHADOW_FLAGS_TEXTURE_DIRTY = (CLIENT_SHADOW_FLAGS_LAST_FLAG << 1),
void PrioritySort();
SHADOW_FLAGS_BRUSH_MODEL = (CLIENT_SHADOW_FLAGS_LAST_FLAG << 2),  
SHADOW_FLAGS_USING_LOD_SHADOW = (CLIENT_SHADOW_FLAGS_LAST_FLAG << 3),
SHADOW_FLAGS_LIGHT_WORLD = (CLIENT_SHADOW_FLAGS_LAST_FLAG << 4),
};


struct ClientShadow_t
CUtlVector<VisibleShadowInfo_t> m_ShadowsInView;
{
CUtlVector<int> m_PriorityIndex;
ClientEntityHandle_t m_Entity;
};
ShadowHandle_t m_ShadowHandle;
ClientLeafShadowHandle_t m_ClientLeafShadowHandle;
unsigned short m_Flags;
VMatrix m_WorldToShadow;
Vector2D m_WorldSize;
Vector m_ShadowDir;
Vector m_LastOrigin;
QAngle m_LastAngles;
Vector m_CurrentLightPos; // When shadowing from local lights, stores the position of the currently shadowing light
        Vector m_TargetLightPos; // When shadowing from local lights, stores the position of the new shadowing light
        float m_LightPosLerp; // Lerp progress when going from current to target light
TextureHandle_t m_ShadowTexture;
CTextureReference m_ShadowDepthTexture;
int m_nRenderFrame;
EHANDLE m_hTargetEntity;
};


private:
//-----------------------------------------------------------------------------
// Shadow update functions
// Singleton instances of shadow and shadow frustum lists
void UpdateStudioShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle );
//-----------------------------------------------------------------------------
void UpdateBrushShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle );
static CVisibleShadowList s_VisibleShadowList;
void UpdateShadow( ClientShadowHandle_t handle, bool force );


// Gets the entity whose shadow this shadow will render into
//-----------------------------------------------------------------------------
IClientRenderable *GetParentShadowEntity( ClientShadowHandle_t handle );
//
//-----------------------------------------------------------------------------
static CUtlVector<C_BaseAnimating *> s_NPCShadowBoneSetups;
static CUtlVector<C_BaseAnimating *> s_NonNPCShadowBoneSetups;


// Adds the child bounds to the bounding box
//-----------------------------------------------------------------------------
void AddChildBounds( matrix3x4_t &matWorldToBBox, IClientRenderable* pParent, Vector &vecMins, Vector &vecMaxs );
// CVisibleShadowList - Constructor and Accessors
//-----------------------------------------------------------------------------
CVisibleShadowList::CVisibleShadowList() : m_ShadowsInView( 0, 64 ), m_PriorityIndex( 0, 64 )  
{
}


// Compute a bounds for the entity + children
int CVisibleShadowList::GetVisibleShadowCount() const
void ComputeHierarchicalBounds( IClientRenderable *pRenderable, Vector &vecMins, Vector &vecMaxs );
{
return m_ShadowsInView.Count();
}


// Builds matrices transforming from world space to shadow space
const VisibleShadowInfo_t &CVisibleShadowList::GetVisibleShadow( int i ) const
void BuildGeneralWorldToShadowMatrix( VMatrix& matWorldToShadow,
{
const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec );
return m_ShadowsInView[m_PriorityIndex[i]];
}


void BuildWorldToShadowMatrix( VMatrix& matWorldToShadow, const Vector& origin, const Quaternion& quatOrientation );
//-----------------------------------------------------------------------------
// CVisibleShadowList - Computes approximate screen area of the shadow
//-----------------------------------------------------------------------------
float CVisibleShadowList::ComputeScreenArea( const Vector &vecCenter, float r ) const
{
CMatRenderContextPtr pRenderContext( materials );
float flScreenDiameter = pRenderContext->ComputePixelDiameterOfSphere( vecCenter, r );
return flScreenDiameter * flScreenDiameter;
}


void BuildPerspectiveWorldToFlashlightMatrix( VMatrix& matWorldToShadow, const FlashlightState_t &flashlightState );
//-----------------------------------------------------------------------------
// CVisibleShadowList - Visits every shadow in the list of leaves
//-----------------------------------------------------------------------------
void CVisibleShadowList::EnumShadow( unsigned short clientShadowHandle )
{
CClientShadowMgr::ClientShadow_t& shadow = s_ClientShadowMgr.m_Shadows[clientShadowHandle];


// Update a shadow
// Don't bother if we rendered it this frame, no matter which view it was rendered for
void UpdateProjectedTextureInternal( ClientShadowHandle_t handle, bool force );
if ( shadow.m_nRenderFrame == gpGlobals->framecount )
return;


// Compute the shadow origin and attenuation start distance
// We don't need to bother with it if it's not render-to-texture
float ComputeLocalShadowOrigin( IClientRenderable* pRenderable,
if ( s_ClientShadowMgr.GetActualShadowCastType( clientShadowHandle ) != SHADOWS_RENDER_TO_TEXTURE )
const Vector& mins, const Vector& maxs, const Vector& localShadowDir, float backupFactor, Vector& origin );
return;


// Remove a shadow from the dirty list
// Don't bother with it if the shadow is totally transparent
void RemoveShadowFromDirtyList( ClientShadowHandle_t handle );
const ShadowInfo_t &shadowInfo = shadowmgr->GetInfo( shadow.m_ShadowHandle );
if ( shadowInfo.m_FalloffBias == 255 )
return;


// NOTE: this will ONLY return SHADOWS_NONE, SHADOWS_SIMPLE, or SHADOW_RENDER_TO_TEXTURE.
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
ShadowType_t GetActualShadowCastType( ClientShadowHandle_t handle ) const;
Assert( pRenderable );
ShadowType_t GetActualShadowCastType( IClientRenderable *pRenderable ) const;


// Builds a simple blobby shadow
// Don't bother with children of hierarchy; they will be drawn with their parents
void BuildOrthoShadow( IClientRenderable* pRenderable, ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs);
if ( s_ClientShadowMgr.ShouldUseParentShadow( pRenderable ) || s_ClientShadowMgr.WillParentRenderBlobbyShadow( pRenderable ) )
return;


// Builds a more complex shadow...
// Compute a sphere surrounding the shadow
void BuildRenderToTextureShadow( IClientRenderable* pRenderable,  
// FIXME: This doesn't account for children of hierarchy... too bad!
ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs );
Vector vecAbsCenter;
float flRadius;
s_ClientShadowMgr.ComputeBoundingSphere( pRenderable, vecAbsCenter, flRadius );


// Build a projected-texture flashlight
// Compute a box surrounding the shadow
void BuildFlashlight( ClientShadowHandle_t handle );
Vector vecAbsMins, vecAbsMaxs;
s_ClientShadowMgr.ComputeShadowBBox( pRenderable, shadow.m_ShadowHandle, vecAbsCenter, flRadius, &vecAbsMins, &vecAbsMaxs );


// Does all the lovely stuff we need to do to have render-to-texture shadows
// FIXME: Add distance check here?
void SetupRenderToTextureShadow( ClientShadowHandle_t h );
void CleanUpRenderToTextureShadow( ClientShadowHandle_t h );


// Compute the extra shadow planes
// Make sure it's in the frustum. If it isn't it's not interesting
void ComputeExtraClipPlanes( IClientRenderable* pRenderable,
if (engine->CullBox( vecAbsMins, vecAbsMaxs ))
ClientShadowHandle_t handle, const Vector* vec,  
return;
const Vector& mins, const Vector& maxs, const Vector& localShadowDir );


// Set extra clip planes related to shadows...
int i = m_ShadowsInView.AddToTail( );
void ClearExtraClipPlanes( ClientShadowHandle_t h );
VisibleShadowInfo_t &info = m_ShadowsInView[i];
void AddExtraClipPlane( ClientShadowHandle_t h, const Vector& normal, float dist );
info.m_hShadow = clientShadowHandle;
m_ShadowsInView[i].m_flArea = ComputeScreenArea( vecAbsCenter, flRadius );


// Cull if the origin is on the wrong side of a shadow clip plane....
// Har, har. When water is rendering (or any multipass technique),
bool CullReceiver( ClientShadowHandle_t handle, IClientRenderable* pRenderable, IClientRenderable* pSourceRenderable );
// we may well initially render from a viewpoint which doesn't include this shadow.  
// That doesn't mean we shouldn't check it again though. Sucks that we need to compute
// the sphere + bbox multiply times though.
shadow.m_nRenderFrame = gpGlobals->framecount;
}


bool ComputeSeparatingPlane( IClientRenderable* pRend1, IClientRenderable* pRend2, cplane_t* pPlane );
//-----------------------------------------------------------------------------
// CVisibleShadowList - Sort based on screen area/priority
//-----------------------------------------------------------------------------
void CVisibleShadowList::PrioritySort()
{
int nCount = m_ShadowsInView.Count();
m_PriorityIndex.EnsureCapacity( nCount );


// Causes all shadows to be re-updated
m_PriorityIndex.RemoveAll();
void UpdateAllShadows();


// One of these gets called with every shadow that potentially will need to re-render
int i, j;
bool DrawRenderToTextureShadow( unsigned short clientShadowHandle, float flArea );
for ( i = 0; i < nCount; ++i )
void DrawRenderToTextureShadowLOD( unsigned short clientShadowHandle );
{
m_PriorityIndex.AddToTail(i);
}


// Draws all children shadows into our own
for ( i = 0; i < nCount - 1; ++i )
bool DrawShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild = false );
{
int nLargestInd = i;
float flLargestArea = m_ShadowsInView[m_PriorityIndex[i]].m_flArea;
for ( j = i + 1; j < nCount; ++j )
{
int nIndex = m_PriorityIndex[j];
if ( flLargestArea < m_ShadowsInView[nIndex].m_flArea )
{
nLargestInd = j;
flLargestArea = m_ShadowsInView[nIndex].m_flArea;
}
}
::V_swap( m_PriorityIndex[i], m_PriorityIndex[nLargestInd] );
}
}


// Setup stage for threading
//-----------------------------------------------------------------------------
bool BuildSetupListForRenderToTextureShadow( unsigned short clientShadowHandle, float flArea );
// CVisibleShadowList - Main entry point for finding shadows in the leaf list
bool BuildSetupShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild = false );
//-----------------------------------------------------------------------------
int CVisibleShadowList::FindShadows( const CViewSetup *pView, int nLeafCount, LeafIndex_t *pLeafList )
{
VPROF_BUDGET( "CVisibleShadowList::FindShadows", VPROF_BUDGETGROUP_SHADOW_RENDERING );


// Computes + sets the render-to-texture texcoords
m_ShadowsInView.RemoveAll();
void SetRenderToTextureShadowTexCoords( ShadowHandle_t handle, int x, int y, int w, int h );
ClientLeafSystem()->EnumerateShadowsInLeaves( nLeafCount, pLeafList, this );
 
int nCount = m_ShadowsInView.Count();
// Visualization....
if (nCount != 0)
void DrawRenderToTextureDebugInfo( IClientRenderable* pRenderable, const Vector& mins, const Vector& maxs );
 
// Advance frame
void AdvanceFrame();
 
// Returns renderable-specific shadow info
float GetShadowDistance( IClientRenderable *pRenderable ) const;
const Vector &GetShadowDirection( IClientRenderable *pRenderable ) const;
const Vector &GetShadowDirection( ClientShadowHandle_t shadowHandle ) const;
 
// Initialize, shutdown render-to-texture shadows
void InitDepthTextureShadows();
void ShutdownDepthTextureShadows();
 
// Initialize, shutdown render-to-texture shadows
void InitRenderToTextureShadows();
void ShutdownRenderToTextureShadows();
 
static bool ShadowHandleCompareFunc( const ClientShadowHandle_t& lhs, const ClientShadowHandle_t& rhs )
{
{
return lhs < rhs;
// Sort based on screen area/priority
PrioritySort();
}
}
return nCount;
}


ClientShadowHandle_t CreateProjectedTexture( ClientEntityHandle_t entity, int flags );
//-----------------------------------------------------------------------------
 
// Constructor
// Lock down the usage of a shadow depth texture...must be unlocked use on subsequent views / frames
//-----------------------------------------------------------------------------
bool LockShadowDepthTexture( CTextureReference *shadowDepthTexture );
CClientShadowMgr::CClientShadowMgr() :
void UnlockAllShadowDepthTextures();
m_DirtyShadows( 0, 0, ShadowHandleCompareFunc ),
m_RenderToTextureActive( false ),
m_bDepthTextureActive( false )
{
m_nDepthTextureResolution = r_flashlightdepthres.GetInt();
m_bThreaded = false;
m_bShadowFromWorldLights = r_worldlight_castshadows.GetBool();
}


// Set and clear flashlight target renderable
//-----------------------------------------------------------------------------
void SetFlashlightTarget( ClientShadowHandle_t shadowHandle, EHANDLE targetEntity );
// Changes the shadow direction...
//-----------------------------------------------------------------------------
CON_COMMAND_F( r_shadowdir, "Set shadow direction", FCVAR_CHEAT )
{
Vector dir;
if ( args.ArgC() == 1 )
{
Vector dir = s_ClientShadowMgr.GetShadowDirection();
Msg( "%.2f %.2f %.2f\n", dir.x, dir.y, dir.z );
return;
}


// Set flashlight light world flag
if ( args.ArgC() == 4 )
void SetFlashlightLightWorld( ClientShadowHandle_t shadowHandle, bool bLightWorld );
{
dir.x = atof( args[1] );
dir.y = atof( args[2] );
dir.z = atof( args[3] );
s_ClientShadowMgr.SetShadowDirection(dir);
}
}


bool IsFlashlightTarget( ClientShadowHandle_t shadowHandle, IClientRenderable *pRenderable );
CON_COMMAND_F( r_shadowangles, "Set shadow angles", FCVAR_CHEAT )
 
{
// Builds a list of active shadows requiring shadow depth renders
Vector dir;
int BuildActiveShadowDepthList( const CViewSetup &viewSetup, int nMaxDepthShadows, ClientShadowHandle_t *pActiveDepthShadows );
QAngle angles;
if (args.ArgC() == 1)
{
Vector dir = s_ClientShadowMgr.GetShadowDirection();
QAngle angles;
VectorAngles( dir, angles );
Msg( "%.2f %.2f %.2f\n", angles.x, angles.y, angles.z );
return;
}


// Sets the view's active flashlight render state
if (args.ArgC() == 4)
void SetViewFlashlightState( int nActiveFlashlightCount, ClientShadowHandle_t* pActiveFlashlights );
{
void UpdateDirtyShadow( ClientShadowHandle_t handle );
angles.x = atof( args[1] );
    void UpdateShadowDirectionFromLocalLightSource( ClientShadowHandle_t shadowHandle );
angles.y = atof( args[2] );
angles.z = atof( args[3] );
AngleVectors( angles, &dir );
s_ClientShadowMgr.SetShadowDirection(dir);
}
}


private:
CON_COMMAND_F( r_shadowcolor, "Set shadow color", FCVAR_CHEAT )
Vector m_SimpleShadowDir;
{
color32 m_AmbientLightColor;
if (args.ArgC() == 1)
CMaterialReference m_SimpleShadow;
{
CMaterialReference m_RenderShadow;
unsigned char r, g, b;
CMaterialReference m_RenderModelShadow;
s_ClientShadowMgr.GetShadowColor( &r, &g, &b );
CTextureReference m_DummyColorTexture;
Msg( "Shadow color %d %d %d\n", r, g, b );
CUtlLinkedList< ClientShadow_t, ClientShadowHandle_t > m_Shadows;
return;
CTextureAllocator m_ShadowAllocator;
}


bool m_RenderToTextureActive;
if (args.ArgC() == 4)
bool m_bRenderTargetNeedsClear;
{
bool m_bUpdatingDirtyShadows;
int r = atoi( args[1] );
bool m_bThreaded;
int g = atoi( args[2] );
float m_flShadowCastDist;
int b = atoi( args[3] );
float m_flMinShadowArea;
s_ClientShadowMgr.SetShadowColor(r, g, b);
CUtlRBTree< ClientShadowHandle_t, unsigned short > m_DirtyShadows;
}
CUtlVector< ClientShadowHandle_t > m_TransparentShadows;
}


// These members maintain current state of depth texturing (size and global active state)
CON_COMMAND_F( r_shadowdist, "Set shadow distance", FCVAR_CHEAT )
// If either changes in a frame, PreRender() will catch it and do the appropriate allocation, deallocation or reallocation
{
bool m_bDepthTextureActive;
if (args.ArgC() == 1)
int m_nDepthTextureResolution; // Assume square (height == width)
{
bool m_bShadowFromWorldLights;
float flDist = s_ClientShadowMgr.GetShadowDistance( );
Msg( "Shadow distance %.2f\n", flDist );
return;
}


CUtlVector< CTextureReference > m_DepthTextureCache;
if (args.ArgC() == 2)
CUtlVector< bool > m_DepthTextureCacheLocks;
{
int m_nMaxDepthTextureShadows;
float flDistance = atof( args[1] );
s_ClientShadowMgr.SetShadowDistance( flDistance );
}
}


friend class CVisibleShadowList;
CON_COMMAND_F( r_shadowblobbycutoff, "some shadow stuff", FCVAR_CHEAT )
friend class CVisibleShadowFrustumList;
{
};
if (args.ArgC() == 1)
{
float flArea = s_ClientShadowMgr.GetBlobbyCutoffArea( );
Msg( "Cutoff area %.2f\n", flArea );
return;
}


//-----------------------------------------------------------------------------
if (args.ArgC() == 2)
// Singleton
{
//-----------------------------------------------------------------------------
float flArea = atof( args[1] );
static CClientShadowMgr s_ClientShadowMgr;
s_ClientShadowMgr.SetShadowBlobbyCutoffArea( flArea );
IClientShadowMgr* g_pClientShadowMgr = &s_ClientShadowMgr;
}
}


static void ShadowRestoreFunc( int nChangeFlags )
{
s_ClientShadowMgr.RestoreRenderState();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Builds a list of potential shadows that lie within our PVS + view frustum
// Initialization, shutdown
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
struct VisibleShadowInfo_t
bool CClientShadowMgr::Init()
{
{
ClientShadowHandle_t m_hShadow;
m_bRenderTargetNeedsClear = false;
float m_flArea;
m_SimpleShadow.Init( "decals/simpleshadow", TEXTURE_GROUP_DECAL );
Vector m_vecAbsCenter;
};


class CVisibleShadowList : public IClientLeafShadowEnum
Vector dir( 0.1, 0.1, -1 );
{
SetShadowDirection(dir);
public:
SetShadowDistance( 50 );


CVisibleShadowList();
SetShadowBlobbyCutoffArea( 0.005 );
int FindShadows( const CViewSetup *pView, int nLeafCount, LeafIndex_t *pLeafList );
int GetVisibleShadowCount() const;


const VisibleShadowInfo_t &GetVisibleShadow( int i ) const;
bool bTools = CommandLine()->CheckParm( "-tools" ) != NULL;
m_nMaxDepthTextureShadows = bTools ? 4 : 1; // Just one shadow depth texture in games, more in tools


private:
bool bLowEnd = ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 );
void EnumShadow( unsigned short clientShadowHandle );
float ComputeScreenArea( const Vector &vecCenter, float r ) const;
void PrioritySort();


CUtlVector<VisibleShadowInfo_t> m_ShadowsInView;
if ( !bLowEnd && r_shadowrendertotexture.GetBool() )
CUtlVector<int> m_PriorityIndex;
{
};
InitRenderToTextureShadows();
}


// If someone turned shadow depth mapping on but we can't do it, force it off
if ( r_flashlightdepthtexture.GetBool() && !materials->SupportsShadowDepthTextures() )
{
r_flashlightdepthtexture.SetValue( 0 );
ShutdownDepthTextureShadows();
}


//-----------------------------------------------------------------------------
if ( !bLowEnd && r_flashlightdepthtexture.GetBool() )
// Singleton instances of shadow and shadow frustum lists
{
//-----------------------------------------------------------------------------
InitDepthTextureShadows();
static CVisibleShadowList s_VisibleShadowList;
}


//-----------------------------------------------------------------------------
materials->AddRestoreFunc( ShadowRestoreFunc );
//
//-----------------------------------------------------------------------------
static CUtlVector<C_BaseAnimating *> s_NPCShadowBoneSetups;
static CUtlVector<C_BaseAnimating *> s_NonNPCShadowBoneSetups;


//-----------------------------------------------------------------------------
return true;
// CVisibleShadowList - Constructor and Accessors
//-----------------------------------------------------------------------------
CVisibleShadowList::CVisibleShadowList() : m_ShadowsInView( 0, 64 ), m_PriorityIndex( 0, 64 )
{
}
}


int CVisibleShadowList::GetVisibleShadowCount() const
void CClientShadowMgr::Shutdown()
{
{
return m_ShadowsInView.Count();
m_SimpleShadow.Shutdown();
m_Shadows.RemoveAll();
ShutdownRenderToTextureShadows();
 
ShutdownDepthTextureShadows();
 
materials->RemoveRestoreFunc( ShadowRestoreFunc );
}
}


const VisibleShadowInfo_t &CVisibleShadowList::GetVisibleShadow( int i ) const
{
return m_ShadowsInView[m_PriorityIndex[i]];
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// CVisibleShadowList - Computes approximate screen area of the shadow
// Initialize, shutdown depth-texture shadows
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
float CVisibleShadowList::ComputeScreenArea( const Vector &vecCenter, float r ) const
void CClientShadowMgr::InitDepthTextureShadows()
{
{
CMatRenderContextPtr pRenderContext( materials );
VPROF_BUDGET( "CClientShadowMgr::InitDepthTextureShadows", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
float flScreenDiameter = pRenderContext->ComputePixelDiameterOfSphere( vecCenter, r );
return flScreenDiameter * flScreenDiameter;
}


if( !m_bDepthTextureActive )
{
m_bDepthTextureActive = true;


//-----------------------------------------------------------------------------
ImageFormat dstFormat  = materials->GetShadowDepthTextureFormat(); // Vendor-dependent depth texture format
// CVisibleShadowList - Visits every shadow in the list of leaves
#if !defined( _X360 )
//-----------------------------------------------------------------------------
ImageFormat nullFormat = materials->GetNullTextureFormat(); // Vendor-dependent null texture format (takes as little memory as possible)
void CVisibleShadowList::EnumShadow( unsigned short clientShadowHandle )
#endif
{
materials->BeginRenderTargetAllocation();
CClientShadowMgr::ClientShadow_t& shadow = s_ClientShadowMgr.m_Shadows[clientShadowHandle];


// Don't bother if we rendered it this frame, no matter which view it was rendered for
#if defined( _X360 )
if ( shadow.m_nRenderFrame == gpGlobals->framecount )
// For the 360, we'll be rendering depth directly into the dummy depth and Resolve()ing to the depth texture.
return;
// only need the dummy surface, don't care about color results
m_DummyColorTexture.InitRenderTargetTexture( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), RT_SIZE_OFFSCREEN, IMAGE_FORMAT_BGR565, MATERIAL_RT_DEPTH_SHARED, false, "_rt_ShadowDummy" );
m_DummyColorTexture.InitRenderTargetSurface( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), IMAGE_FORMAT_BGR565, true );
#else
m_DummyColorTexture.InitRenderTarget( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), RT_SIZE_OFFSCREEN, nullFormat, MATERIAL_RT_DEPTH_NONE, false, "_rt_ShadowDummy" );
#endif


// We don't need to bother with it if it's not render-to-texture
// Create some number of depth-stencil textures
if ( s_ClientShadowMgr.GetActualShadowCastType( clientShadowHandle ) != SHADOWS_RENDER_TO_TEXTURE )
m_DepthTextureCache.Purge();
return;
m_DepthTextureCacheLocks.Purge();
for( int i=0; i < m_nMaxDepthTextureShadows; i++ )
{
CTextureReference depthTex; // Depth-stencil surface
bool bFalse = false;


// Don't bother with it if the shadow is totally transparent
char strRTName[64];
const ShadowInfo_t &shadowInfo = shadowmgr->GetInfo( shadow.m_ShadowHandle );
Q_snprintf( strRTName, ARRAYSIZE( strRTName ), "_rt_ShadowDepthTexture_%d", i );
if ( shadowInfo.m_FalloffBias == 255 )
return;


IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
#if defined( _X360 )
Assert( pRenderable );
// create a render target to use as a resolve target to get the shared depth buffer
// surface is effectively never used
depthTex.InitRenderTargetTexture( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_OFFSCREEN, dstFormat, MATERIAL_RT_DEPTH_NONE, false, strRTName );
depthTex.InitRenderTargetSurface( 1, 1, dstFormat, false );
#else
depthTex.InitRenderTarget( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_OFFSCREEN, dstFormat, MATERIAL_RT_DEPTH_NONE, false, strRTName );
#endif


// Don't bother with children of hierarchy; they will be drawn with their parents
if ( i == 0 )
if ( s_ClientShadowMgr.ShouldUseParentShadow( pRenderable ) || s_ClientShadowMgr.WillParentRenderBlobbyShadow( pRenderable ) )
{
return;
// Shadow may be resized during allocation (due to resolution constraints etc)
m_nDepthTextureResolution = depthTex->GetActualWidth();
r_flashlightdepthres.SetValue( m_nDepthTextureResolution );
}


// Compute a sphere surrounding the shadow
m_DepthTextureCache.AddToTail( depthTex );
// FIXME: This doesn't account for children of hierarchy... too bad!
m_DepthTextureCacheLocks.AddToTail( bFalse );
Vector vecAbsCenter;
}
float flRadius;
s_ClientShadowMgr.ComputeBoundingSphere( pRenderable, vecAbsCenter, flRadius );


// Compute a box surrounding the shadow
materials->EndRenderTargetAllocation();
Vector vecAbsMins, vecAbsMaxs;
}
s_ClientShadowMgr.ComputeShadowBBox( pRenderable, shadow.m_ShadowHandle, vecAbsCenter, flRadius, &vecAbsMins, &vecAbsMaxs );
}


// FIXME: Add distance check here?
void CClientShadowMgr::ShutdownDepthTextureShadows()
{
if( m_bDepthTextureActive )
{
// Shut down the dummy texture
m_DummyColorTexture.Shutdown();


// Make sure it's in the frustum. If it isn't it's not interesting
while( m_DepthTextureCache.Count() )
if (engine->CullBox( vecAbsMins, vecAbsMaxs ))
{
return;
m_DepthTextureCache[ m_DepthTextureCache.Count()-1 ].Shutdown();


int i = m_ShadowsInView.AddToTail( );
m_DepthTextureCacheLocks.Remove( m_DepthTextureCache.Count()-1 );
VisibleShadowInfo_t &info = m_ShadowsInView[i];
m_DepthTextureCache.Remove( m_DepthTextureCache.Count()-1 );
info.m_hShadow = clientShadowHandle;
}
m_ShadowsInView[i].m_flArea = ComputeScreenArea( vecAbsCenter, flRadius );


// Har, har. When water is rendering (or any multipass technique),
m_bDepthTextureActive = false;
// we may well initially render from a viewpoint which doesn't include this shadow.
}
// That doesn't mean we shouldn't check it again though. Sucks that we need to compute
// the sphere + bbox multiply times though.
shadow.m_nRenderFrame = gpGlobals->framecount;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// CVisibleShadowList - Sort based on screen area/priority
// Initialize, shutdown render-to-texture shadows
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CVisibleShadowList::PrioritySort()
void CClientShadowMgr::InitRenderToTextureShadows()
{
{
int nCount = m_ShadowsInView.Count();
if ( !m_RenderToTextureActive )
m_PriorityIndex.EnsureCapacity( nCount );
{
m_RenderToTextureActive = true;
m_RenderShadow.Init( "decals/rendershadow", TEXTURE_GROUP_DECAL );
m_RenderModelShadow.Init( "decals/rendermodelshadow", TEXTURE_GROUP_DECAL );
m_ShadowAllocator.Init();


m_PriorityIndex.RemoveAll();
m_ShadowAllocator.Reset();
m_bRenderTargetNeedsClear = true;


int i, j;
float fr = (float)m_AmbientLightColor.r / 255.0f;
for ( i = 0; i < nCount; ++i )
float fg = (float)m_AmbientLightColor.g / 255.0f;
{
float fb = (float)m_AmbientLightColor.b / 255.0f;
m_PriorityIndex.AddToTail(i);
m_RenderShadow->ColorModulate( fr, fg, fb );
}
m_RenderModelShadow->ColorModulate( fr, fg, fb );


for ( i = 0; i < nCount - 1; ++i )
// Iterate over all existing textures and allocate shadow textures
{
for (ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
int nLargestInd = i;
float flLargestArea = m_ShadowsInView[m_PriorityIndex[i]].m_flArea;
for ( j = i + 1; j < nCount; ++j )
{
{
int nIndex = m_PriorityIndex[j];
ClientShadow_t& shadow = m_Shadows[i];
if ( flLargestArea < m_ShadowsInView[nIndex].m_flArea )
if ( shadow.m_Flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE )
{
{
nLargestInd = j;
SetupRenderToTextureShadow( i );
flLargestArea = m_ShadowsInView[nIndex].m_flArea;
MarkRenderToTextureShadowDirty( i );
 
// Switch the material to use render-to-texture shadows
shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_RenderShadow, m_RenderModelShadow, (void*)(uintp)i );
}
}
}
}
::V_swap( m_PriorityIndex[i], m_PriorityIndex[nLargestInd] );
}
}
}
}


 
void CClientShadowMgr::ShutdownRenderToTextureShadows()
//-----------------------------------------------------------------------------
// CVisibleShadowList - Main entry point for finding shadows in the leaf list
//-----------------------------------------------------------------------------
int CVisibleShadowList::FindShadows( const CViewSetup *pView, int nLeafCount, LeafIndex_t *pLeafList )
{
{
VPROF_BUDGET( "CVisibleShadowList::FindShadows", VPROF_BUDGETGROUP_SHADOW_RENDERING );
if (m_RenderToTextureActive)
{
// Iterate over all existing textures and deallocate shadow textures
for (ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
{
CleanUpRenderToTextureShadow( i );


m_ShadowsInView.RemoveAll();
// Switch the material to use blobby shadows
ClientLeafSystem()->EnumerateShadowsInLeaves( nLeafCount, pLeafList, this );
ClientShadow_t& shadow = m_Shadows[i];
int nCount = m_ShadowsInView.Count();
shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_SimpleShadow, m_SimpleShadow, (void*)CLIENTSHADOW_INVALID_HANDLE );
if (nCount != 0)
shadowmgr->SetShadowTexCoord( shadow.m_ShadowHandle, 0, 0, 1, 1 );
{
ClearExtraClipPlanes( i );
// Sort based on screen area/priority
}
PrioritySort();
 
m_RenderShadow.Shutdown();
m_RenderModelShadow.Shutdown();
 
m_ShadowAllocator.DeallocateAllTextures();
m_ShadowAllocator.Shutdown();
 
// Cause the render target to go away
materials->UncacheUnusedMaterials();
 
m_RenderToTextureActive = false;
}
}
return nCount;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Constructor
// Sets the shadow color
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CClientShadowMgr::CClientShadowMgr() :
void CClientShadowMgr::SetShadowColor( unsigned char r, unsigned char g, unsigned char b )
m_DirtyShadows( 0, 0, ShadowHandleCompareFunc ),
m_RenderToTextureActive( false ),
m_bDepthTextureActive( false )
{
{
m_nDepthTextureResolution = r_flashlightdepthres.GetInt();
float fr = (float)r / 255.0f;
m_bThreaded = false;
float fg = (float)g / 255.0f;
m_bShadowFromWorldLights = r_worldlight_castshadows.GetBool();
float fb = (float)b / 255.0f;
}


 
// Hook the shadow color into the shadow materials
m_SimpleShadow->ColorModulate( fr, fg, fb );


//-----------------------------------------------------------------------------
if (m_RenderToTextureActive)
// Changes the shadow direction...
{
//-----------------------------------------------------------------------------
m_RenderShadow->ColorModulate( fr, fg, fb );
CON_COMMAND_F( r_shadowdir, "Set shadow direction", FCVAR_CHEAT )
m_RenderModelShadow->ColorModulate( fr, fg, fb );
{
Vector dir;
if ( args.ArgC() == 1 )
{
Vector dir = s_ClientShadowMgr.GetShadowDirection();
Msg( "%.2f %.2f %.2f\n", dir.x, dir.y, dir.z );
return;
}
}


if ( args.ArgC() == 4 )
m_AmbientLightColor.r = r;
{
m_AmbientLightColor.g = g;
dir.x = atof( args[1] );
m_AmbientLightColor.b = b;
dir.y = atof( args[2] );
dir.z = atof( args[3] );
s_ClientShadowMgr.SetShadowDirection(dir);
}
}
}


CON_COMMAND_F( r_shadowangles, "Set shadow angles", FCVAR_CHEAT )
void CClientShadowMgr::GetShadowColor( unsigned char *r, unsigned char *g, unsigned char *b ) const
{
{
Vector dir;
*r = m_AmbientLightColor.r;
QAngle angles;
*g = m_AmbientLightColor.g;
if (args.ArgC() == 1)
*b = m_AmbientLightColor.b;
{
}
Vector dir = s_ClientShadowMgr.GetShadowDirection();
QAngle angles;
VectorAngles( dir, angles );
Msg( "%.2f %.2f %.2f\n", angles.x, angles.y, angles.z );
return;
}


if (args.ArgC() == 4)
//-----------------------------------------------------------------------------
{
// Level init... get the shadow color
angles.x = atof( args[1] );
//-----------------------------------------------------------------------------
angles.y = atof( args[2] );
void CClientShadowMgr::LevelInitPreEntity()
angles.z = atof( args[3] );
{
AngleVectors( angles, &dir );
m_bUpdatingDirtyShadows = false;
s_ClientShadowMgr.SetShadowDirection(dir);
 
}
Vector ambientColor;
}
engine->GetAmbientLightColor( ambientColor );
ambientColor *= 3;
ambientColor += Vector( 0.3f, 0.3f, 0.3f );
 
unsigned char r = ambientColor[0] > 1.0 ? 255 : 255 * ambientColor[0];
unsigned char g = ambientColor[1] > 1.0 ? 255 : 255 * ambientColor[1];
unsigned char b = ambientColor[2] > 1.0 ? 255 : 255 * ambientColor[2];


CON_COMMAND_F( r_shadowcolor, "Set shadow color", FCVAR_CHEAT )
SetShadowColor(r, g, b);
{
if (args.ArgC() == 1)
{
unsigned char r, g, b;
s_ClientShadowMgr.GetShadowColor( &r, &g, &b );
Msg( "Shadow color %d %d %d\n", r, g, b );
return;
}


if (args.ArgC() == 4)
// Set up the texture allocator
if ( m_RenderToTextureActive )
{
{
int r = atoi( args[1] );
m_ShadowAllocator.Reset();
int g = atoi( args[2] );
m_bRenderTargetNeedsClear = true;
int b = atoi( args[3] );
s_ClientShadowMgr.SetShadowColor(r, g, b);
}
}
}
}


CON_COMMAND_F( r_shadowdist, "Set shadow distance", FCVAR_CHEAT )
//-----------------------------------------------------------------------------
// Clean up all shadows
//-----------------------------------------------------------------------------
void CClientShadowMgr::LevelShutdownPostEntity()
{
{
if (args.ArgC() == 1)
// All shadows *should* have been cleaned up when the entities went away
// but, just in case....
Assert( m_Shadows.Count() == 0 );
 
ClientShadowHandle_t h = m_Shadows.Head();
while (h != CLIENTSHADOW_INVALID_HANDLE)
{
{
float flDist = s_ClientShadowMgr.GetShadowDistance( );
ClientShadowHandle_t next = m_Shadows.Next(h);
Msg( "Shadow distance %.2f\n", flDist );
DestroyShadow( h );
return;
h = next;
}
}


if (args.ArgC() == 2)
// Deallocate all textures
if (m_RenderToTextureActive)
{
{
float flDistance = atof( args[1] );
m_ShadowAllocator.DeallocateAllTextures();
s_ClientShadowMgr.SetShadowDistance( flDistance );
}
}
r_shadows_gamecontrol.SetValue( -1 );
}
}


CON_COMMAND_F( r_shadowblobbycutoff, "some shadow stuff", FCVAR_CHEAT )
//-----------------------------------------------------------------------------
// Deals with alt-tab
//-----------------------------------------------------------------------------
void CClientShadowMgr::RestoreRenderState()
{
{
if (args.ArgC() == 1)
// Mark all shadows dirty; they need to regenerate their state
ClientShadowHandle_t h;
for ( h = m_Shadows.Head(); h != m_Shadows.InvalidIndex(); h = m_Shadows.Next(h) )
{
{
float flArea = s_ClientShadowMgr.GetBlobbyCutoffArea( );
m_Shadows[h].m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;
Msg( "Cutoff area %.2f\n", flArea );
return;
}
}


if (args.ArgC() == 2)
SetShadowColor( m_AmbientLightColor.r, m_AmbientLightColor.g, m_AmbientLightColor.b );
{
m_bRenderTargetNeedsClear = true;
float flArea = atof( args[1] );
s_ClientShadowMgr.SetShadowBlobbyCutoffArea( flArea );
}
}
}


static void ShadowRestoreFunc( int nChangeFlags )
//-----------------------------------------------------------------------------
// Does all the lovely stuff we need to do to have render-to-texture shadows
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetupRenderToTextureShadow( ClientShadowHandle_t h )
{
{
s_ClientShadowMgr.RestoreRenderState();
// First, compute how much texture memory we want to use.
}
ClientShadow_t& shadow = m_Shadows[h];
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
if ( !pRenderable )
return;


//-----------------------------------------------------------------------------
Vector mins, maxs;
// Initialization, shutdown
pRenderable->GetShadowRenderBounds( mins, maxs, GetActualShadowCastType( h ) );
//-----------------------------------------------------------------------------
bool CClientShadowMgr::Init()
{
m_bRenderTargetNeedsClear = false;
m_SimpleShadow.Init( "decals/simpleshadow", TEXTURE_GROUP_DECAL );


Vector dir( 0.1, 0.1, -1 );
// Compute the maximum dimension
SetShadowDirection(dir);
Vector size;
SetShadowDistance( 50 );
VectorSubtract( maxs, mins, size );
float maxSize = MAX( size.x, size.y );
maxSize = MAX( maxSize, size.z );


SetShadowBlobbyCutoffArea( 0.005 );
// Figure out the texture size
 
// For now, we're going to assume a fixed number of shadow texels
bool bTools = CommandLine()->CheckParm( "-tools" ) != NULL;
// per shadow-caster size; add in some extra space at the boundary.
m_nMaxDepthTextureShadows = bTools ? 4 : 1; // Just one shadow depth texture in games, more in tools
int texelCount = TEXEL_SIZE_PER_CASTER_SIZE * maxSize;
 
bool bLowEnd = ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 );
// Pick the first power of 2 larger...
 
int textureSize = 1;
if ( !bLowEnd && r_shadowrendertotexture.GetBool() )
while (textureSize < texelCount)
{
{
InitRenderToTextureShadows();
textureSize <<= 1;
}
}


// If someone turned shadow depth mapping on but we can't do it, force it off
shadow.m_ShadowTexture = m_ShadowAllocator.AllocateTexture( textureSize, textureSize );
if ( r_flashlightdepthtexture.GetBool() && !materials->SupportsShadowDepthTextures() )
}
{
r_flashlightdepthtexture.SetValue( 0 );
ShutdownDepthTextureShadows();
}


if ( !bLowEnd && r_flashlightdepthtexture.GetBool() )
void CClientShadowMgr::CleanUpRenderToTextureShadow( ClientShadowHandle_t h )
{
ClientShadow_t& shadow = m_Shadows[h];
if (m_RenderToTextureActive && (shadow.m_Flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE))
{
{
InitDepthTextureShadows();
m_ShadowAllocator.DeallocateTexture( shadow.m_ShadowTexture );
shadow.m_ShadowTexture = INVALID_TEXTURE_HANDLE;
}
}
materials->AddRestoreFunc( ShadowRestoreFunc );
return true;
}
}


void CClientShadowMgr::Shutdown()
//-----------------------------------------------------------------------------
// Causes all shadows to be re-updated
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateAllShadows()
{
{
m_SimpleShadow.Shutdown();
for ( ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
m_Shadows.RemoveAll();
{
ShutdownRenderToTextureShadows();
ClientShadow_t& shadow = m_Shadows[i];
 
// Don't bother with flashlights
if ( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) != 0 )
continue;


ShutdownDepthTextureShadows();
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
if ( !pRenderable )
continue;


materials->RemoveRestoreFunc( ShadowRestoreFunc );
Assert( pRenderable->GetShadowHandle() == i );
AddToDirtyShadowList( pRenderable, true );
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Initialize, shutdown depth-texture shadows
// Sets the shadow direction
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::InitDepthTextureShadows()
void CClientShadowMgr::SetShadowDirection( const Vector& dir )
{
{
VPROF_BUDGET( "CClientShadowMgr::InitDepthTextureShadows", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
VectorCopy( dir, m_SimpleShadowDir );
VectorNormalize( m_SimpleShadowDir );


if( !m_bDepthTextureActive )
if ( m_RenderToTextureActive )
{
{
m_bDepthTextureActive = true;
UpdateAllShadows();
}
}


ImageFormat dstFormat  = materials->GetShadowDepthTextureFormat(); // Vendor-dependent depth texture format
const Vector &CClientShadowMgr::GetShadowDirection() const
#if !defined( _X360 )
{
ImageFormat nullFormat = materials->GetNullTextureFormat(); // Vendor-dependent null texture format (takes as little memory as possible)
// This will cause blobby shadows to always project straight down
#endif
static Vector s_vecDown( 0, 0, -1 );
materials->BeginRenderTargetAllocation();
if ( !m_RenderToTextureActive )
return s_vecDown;


#if defined( _X360 )
return m_SimpleShadowDir;
// For the 360, we'll be rendering depth directly into the dummy depth and Resolve()ing to the depth texture.
}
// only need the dummy surface, don't care about color results
m_DummyColorTexture.InitRenderTargetTexture( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), RT_SIZE_OFFSCREEN, IMAGE_FORMAT_BGR565, MATERIAL_RT_DEPTH_SHARED, false, "_rt_ShadowDummy" );
m_DummyColorTexture.InitRenderTargetSurface( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), IMAGE_FORMAT_BGR565, true );
#else
m_DummyColorTexture.InitRenderTarget( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), RT_SIZE_OFFSCREEN, nullFormat, MATERIAL_RT_DEPTH_NONE, false, "_rt_ShadowDummy" );
#endif


// Create some number of depth-stencil textures
//-----------------------------------------------------------------------------
m_DepthTextureCache.Purge();
// Gets shadow information for a particular renderable
m_DepthTextureCacheLocks.Purge();
//-----------------------------------------------------------------------------
for( int i=0; i < m_nMaxDepthTextureShadows; i++ )
float CClientShadowMgr::GetShadowDistance( IClientRenderable *pRenderable ) const
{
{
CTextureReference depthTex; // Depth-stencil surface
float flDist = m_flShadowCastDist;
bool bFalse = false;


char strRTName[64];
// Allow the renderable to override the default
Q_snprintf( strRTName, ARRAYSIZE( strRTName ), "_rt_ShadowDepthTexture_%d", i );
pRenderable->GetShadowCastDistance( &flDist, GetActualShadowCastType( pRenderable ) );


#if defined( _X360 )
return flDist;
// create a render target to use as a resolve target to get the shared depth buffer
}
// surface is effectively never used
depthTex.InitRenderTargetTexture( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_OFFSCREEN, dstFormat, MATERIAL_RT_DEPTH_NONE, false, strRTName );
depthTex.InitRenderTargetSurface( 1, 1, dstFormat, false );
#else
depthTex.InitRenderTarget( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_OFFSCREEN, dstFormat, MATERIAL_RT_DEPTH_NONE, false, strRTName );
#endif


if ( i == 0 )
const Vector &CClientShadowMgr::GetShadowDirection( IClientRenderable *pRenderable ) const
{
{
// Shadow may be resized during allocation (due to resolution constraints etc)
Vector &vecResult = AllocTempVector();
m_nDepthTextureResolution = depthTex->GetActualWidth();
vecResult = GetShadowDirection();
r_flashlightdepthres.SetValue( m_nDepthTextureResolution );
}


m_DepthTextureCache.AddToTail( depthTex );
// Allow the renderable to override the default
m_DepthTextureCacheLocks.AddToTail( bFalse );
pRenderable->GetShadowCastDirection( &vecResult, GetActualShadowCastType( pRenderable ) );
}


materials->EndRenderTargetAllocation();
return vecResult;
}
}
}


void CClientShadowMgr::ShutdownDepthTextureShadows()  
//-----------------------------------------------------------------------------
// Sets the shadow distance
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetShadowDistance( float flMaxDistance )
{
{
if( m_bDepthTextureActive )
m_flShadowCastDist = flMaxDistance;
{
UpdateAllShadows();
// Shut down the dummy texture
}
m_DummyColorTexture.Shutdown();


while( m_DepthTextureCache.Count() )
float CClientShadowMgr::GetShadowDistance( ) const
{
{
m_DepthTextureCache[ m_DepthTextureCache.Count()-1 ].Shutdown();
return m_flShadowCastDist;
}


m_DepthTextureCacheLocks.Remove( m_DepthTextureCache.Count()-1 );
//-----------------------------------------------------------------------------
m_DepthTextureCache.Remove( m_DepthTextureCache.Count()-1 );
// Sets the screen area at which blobby shadows are always used
}
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetShadowBlobbyCutoffArea( float flMinArea )
{
m_flMinShadowArea = flMinArea;
}


m_bDepthTextureActive = false;
float CClientShadowMgr::GetBlobbyCutoffArea( ) const
}
{
return m_flMinShadowArea;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Initialize, shutdown render-to-texture shadows
// Purpose:
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::InitRenderToTextureShadows()
void CClientShadowMgr::SetFalloffBias( ClientShadowHandle_t handle, unsigned char ucBias )
{
{
if ( !m_RenderToTextureActive )
shadowmgr->SetFalloffBias( m_Shadows[handle].m_ShadowHandle, ucBias );
{
}
m_RenderToTextureActive = true;
m_RenderShadow.Init( "decals/rendershadow", TEXTURE_GROUP_DECAL );
m_RenderModelShadow.Init( "decals/rendermodelshadow", TEXTURE_GROUP_DECAL );
m_ShadowAllocator.Init();


m_ShadowAllocator.Reset();
//-----------------------------------------------------------------------------
m_bRenderTargetNeedsClear = true;
// Returns the shadow texture
 
//-----------------------------------------------------------------------------
float fr = (float)m_AmbientLightColor.r / 255.0f;
ITexture* CClientShadowMgr::GetShadowTexture( unsigned short h )
float fg = (float)m_AmbientLightColor.g / 255.0f;
{
float fb = (float)m_AmbientLightColor.b / 255.0f;
return m_ShadowAllocator.GetTexture();
m_RenderShadow->ColorModulate( fr, fg, fb );
}
m_RenderModelShadow->ColorModulate( fr, fg, fb );


// Iterate over all existing textures and allocate shadow textures
//-----------------------------------------------------------------------------
for (ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
// Returns information needed by the model proxy
{
//-----------------------------------------------------------------------------
ClientShadow_t& shadow = m_Shadows[i];
const ShadowInfo_t& CClientShadowMgr::GetShadowInfo( ClientShadowHandle_t h )
if ( shadow.m_Flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE )
{
{
return shadowmgr->GetInfo( m_Shadows[h].m_ShadowHandle );
SetupRenderToTextureShadow( i );
MarkRenderToTextureShadowDirty( i );
 
// Switch the material to use render-to-texture shadows
shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_RenderShadow, m_RenderModelShadow, (void*)(uintp)i );
}
}
}
}
}


void CClientShadowMgr::ShutdownRenderToTextureShadows()
//-----------------------------------------------------------------------------
// Renders the shadow texture to screen...
//-----------------------------------------------------------------------------
void CClientShadowMgr::RenderShadowTexture( int w, int h )
{
{
if (m_RenderToTextureActive)
if (m_RenderToTextureActive)
{
{
// Iterate over all existing textures and deallocate shadow textures
CMatRenderContextPtr pRenderContext( materials );
for (ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
pRenderContext->Bind( m_RenderShadow );
{
IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
CleanUpRenderToTextureShadow( i );
 
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );


// Switch the material to use blobby shadows
meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
ClientShadow_t& shadow = m_Shadows[i];
meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_SimpleShadow, m_SimpleShadow, (void*)CLIENTSHADOW_INVALID_HANDLE );
meshBuilder.Color4ub( 0, 0, 0, 0 );
shadowmgr->SetShadowTexCoord( shadow.m_ShadowHandle, 0, 0, 1, 1 );
meshBuilder.AdvanceVertex();
ClearExtraClipPlanes( i );
}


m_RenderShadow.Shutdown();
meshBuilder.Position3f( w, 0.0f, 0.0f );
m_RenderModelShadow.Shutdown();
meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
meshBuilder.Color4ub( 0, 0, 0, 0 );
meshBuilder.AdvanceVertex();


m_ShadowAllocator.DeallocateAllTextures();
meshBuilder.Position3f( w, h, 0.0f );
m_ShadowAllocator.Shutdown();
meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
meshBuilder.Color4ub( 0, 0, 0, 0 );
meshBuilder.AdvanceVertex();


// Cause the render target to go away
meshBuilder.Position3f( 0.0f, h, 0.0f );
materials->UncacheUnusedMaterials();
meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
meshBuilder.Color4ub( 0, 0, 0, 0 );
meshBuilder.AdvanceVertex();


m_RenderToTextureActive = false;
meshBuilder.End();
pMesh->Draw();
}
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Sets the shadow color
// Create/destroy a shadow
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetShadowColor( unsigned char r, unsigned char g, unsigned char b )
ClientShadowHandle_t CClientShadowMgr::CreateProjectedTexture( ClientEntityHandle_t entity, int flags )
{
{
float fr = (float)r / 255.0f;
// We need to know if it's a brush model for shadows
float fg = (float)g / 255.0f;
if( !( flags & SHADOW_FLAGS_FLASHLIGHT ) )
float fb = (float)b / 255.0f;
{
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( entity );
if ( !pRenderable )
return m_Shadows.InvalidIndex();


// Hook the shadow color into the shadow materials
int modelType = modelinfo->GetModelType( pRenderable->GetModel() );
m_SimpleShadow->ColorModulate( fr, fg, fb );
if (modelType == mod_brush)
 
{
if (m_RenderToTextureActive)
flags |= SHADOW_FLAGS_BRUSH_MODEL;
{
}
m_RenderShadow->ColorModulate( fr, fg, fb );
m_RenderModelShadow->ColorModulate( fr, fg, fb );
}
}


m_AmbientLightColor.r = r;
ClientShadowHandle_t h = m_Shadows.AddToTail();
m_AmbientLightColor.g = g;
ClientShadow_t& shadow = m_Shadows[h];
m_AmbientLightColor.b = b;
shadow.m_Entity = entity;
}
shadow.m_ClientLeafShadowHandle = ClientLeafSystem()->AddShadow( h, flags );
shadow.m_Flags = flags;
shadow.m_nRenderFrame = -1;
shadow.m_ShadowDir = GetShadowDirection();
    shadow.m_CurrentLightPos.Init( FLT_MAX, FLT_MAX, FLT_MAX );
    shadow.m_TargetLightPos.Init( FLT_MAX, FLT_MAX, FLT_MAX );
    shadow.m_LightPosLerp = FLT_MAX;
shadow.m_LastOrigin.Init( FLT_MAX, FLT_MAX, FLT_MAX );
shadow.m_LastAngles.Init( FLT_MAX, FLT_MAX, FLT_MAX );
Assert( ( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 ) !=
( ( shadow.m_Flags & SHADOW_FLAGS_SHADOW ) == 0 ) );


void CClientShadowMgr::GetShadowColor( unsigned char *r, unsigned char *g, unsigned char *b ) const
// Set up the flags....
{
IMaterial* pShadowMaterial = m_SimpleShadow;
*r = m_AmbientLightColor.r;
IMaterial* pShadowModelMaterial = m_SimpleShadow;
*g = m_AmbientLightColor.g;
void* pShadowProxyData = (void*)CLIENTSHADOW_INVALID_HANDLE;
*b = m_AmbientLightColor.b;
}


if ( m_RenderToTextureActive && (flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE) )
{
SetupRenderToTextureShadow(h);


//-----------------------------------------------------------------------------
pShadowMaterial = m_RenderShadow;
// Level init... get the shadow color
pShadowModelMaterial = m_RenderModelShadow;
//-----------------------------------------------------------------------------
pShadowProxyData = (void*)(uintp)h;
void CClientShadowMgr::LevelInitPreEntity()
}
{
m_bUpdatingDirtyShadows = false;


Vector ambientColor;
if( flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE )
engine->GetAmbientLightColor( ambientColor );
{
ambientColor *= 3;
pShadowMaterial = m_RenderShadow;
ambientColor += Vector( 0.3f, 0.3f, 0.3f );
pShadowModelMaterial = m_RenderModelShadow;
pShadowProxyData = (void*)(uintp)h;
}


unsigned char r = ambientColor[0] > 1.0 ? 255 : 255 * ambientColor[0];
int createShadowFlags;
unsigned char g = ambientColor[1] > 1.0 ? 255 : 255 * ambientColor[1];
if( flags & SHADOW_FLAGS_FLASHLIGHT )
unsigned char b = ambientColor[2] > 1.0 ? 255 : 255 * ambientColor[2];
{
 
// don't use SHADOW_CACHE_VERTS with projective lightsources since we expect that they will change every frame.
SetShadowColor(r, g, b);
// FIXME: might want to make it cache optionally if it's an entity light that is static.
 
createShadowFlags = SHADOW_FLASHLIGHT;
// Set up the texture allocator
}
if ( m_RenderToTextureActive )
else
{
{
m_ShadowAllocator.Reset();
createShadowFlags = SHADOW_CACHE_VERTS;
m_bRenderTargetNeedsClear = true;
}
}
shadow.m_ShadowHandle = shadowmgr->CreateShadowEx( pShadowMaterial, pShadowModelMaterial, pShadowProxyData, createShadowFlags );
return h;
}
}


ClientShadowHandle_t CClientShadowMgr::CreateFlashlight( const FlashlightState_t &lightState )
{
// We don't really need a model entity handle for a projective light source, so use an invalid one.
static ClientEntityHandle_t invalidHandle = INVALID_CLIENTENTITY_HANDLE;


//-----------------------------------------------------------------------------
int shadowFlags = SHADOW_FLAGS_FLASHLIGHT | SHADOW_FLAGS_LIGHT_WORLD;
// Clean up all shadows
if( lightState.m_bEnableShadows && r_flashlightdepthtexture.GetBool() )
//-----------------------------------------------------------------------------
void CClientShadowMgr::LevelShutdownPostEntity()
{
// All shadows *should* have been cleaned up when the entities went away
// but, just in case....
Assert( m_Shadows.Count() == 0 );
 
ClientShadowHandle_t h = m_Shadows.Head();
while (h != CLIENTSHADOW_INVALID_HANDLE)
{
{
ClientShadowHandle_t next = m_Shadows.Next(h);
shadowFlags |= SHADOW_FLAGS_USE_DEPTH_TEXTURE;
DestroyShadow( h );
h = next;
}
}


// Deallocate all textures
ClientShadowHandle_t shadowHandle = CreateProjectedTexture( invalidHandle, shadowFlags );
if (m_RenderToTextureActive)
 
UpdateFlashlightState( shadowHandle, lightState );
UpdateProjectedTexture( shadowHandle, true );
return shadowHandle;
}
ClientShadowHandle_t CClientShadowMgr::CreateShadow( ClientEntityHandle_t entity, int flags )
{
// We don't really need a model entity handle for a projective light source, so use an invalid one.
flags &= ~SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK;
flags |= SHADOW_FLAGS_SHADOW | SHADOW_FLAGS_TEXTURE_DIRTY;
ClientShadowHandle_t shadowHandle = CreateProjectedTexture( entity, flags );
 
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( entity );
if ( pRenderable )
{
{
m_ShadowAllocator.DeallocateAllTextures();
Assert( !pRenderable->IsShadowDirty( ) );
pRenderable->MarkShadowDirty( true );
}
}


r_shadows_gamecontrol.SetValue( -1 );
// NOTE: We *have* to call the version that takes a shadow handle
// even if we have an entity because this entity hasn't set its shadow handle yet
AddToDirtyShadowList( shadowHandle, true );
return shadowHandle;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Deals with alt-tab
// Updates the flashlight direction and re-computes surfaces it should lie on
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::RestoreRenderState()
void CClientShadowMgr::UpdateFlashlightState( ClientShadowHandle_t shadowHandle, const FlashlightState_t &flashlightState )
{
{
// Mark all shadows dirty; they need to regenerate their state
VPROF_BUDGET( "CClientShadowMgr::UpdateFlashlightState", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
ClientShadowHandle_t h;
for ( h = m_Shadows.Head(); h != m_Shadows.InvalidIndex(); h = m_Shadows.Next(h) )
{
m_Shadows[h].m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;
}


SetShadowColor( m_AmbientLightColor.r, m_AmbientLightColor.g, m_AmbientLightColor.b );
BuildPerspectiveWorldToFlashlightMatrix( m_Shadows[shadowHandle].m_WorldToShadow, flashlightState );
m_bRenderTargetNeedsClear = true;
shadowmgr->UpdateFlashlightState( m_Shadows[shadowHandle].m_ShadowHandle, flashlightState );
}
}


void CClientShadowMgr::DestroyFlashlight( ClientShadowHandle_t shadowHandle )
{
DestroyShadow( shadowHandle );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Does all the lovely stuff we need to do to have render-to-texture shadows
// Remove a shadow from the dirty list
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetupRenderToTextureShadow( ClientShadowHandle_t h )
void CClientShadowMgr::RemoveShadowFromDirtyList( ClientShadowHandle_t handle )
{
{
// First, compute how much texture memory we want to use.
int idx = m_DirtyShadows.Find( handle );
ClientShadow_t& shadow = m_Shadows[h];
if ( idx != m_DirtyShadows.InvalidIndex() )
{
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
// Clean up the shadow update bit.
if ( !pRenderable )
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[handle].m_Entity );
return;
if ( pRenderable )
{
pRenderable->MarkShadowDirty( false );
}
m_DirtyShadows.RemoveAt( idx );
}
}


Vector mins, maxs;
//-----------------------------------------------------------------------------
pRenderable->GetShadowRenderBounds( mins, maxs, GetActualShadowCastType( h ) );
// Remove a shadow
 
//-----------------------------------------------------------------------------
// Compute the maximum dimension
void CClientShadowMgr::DestroyShadow( ClientShadowHandle_t handle )
Vector size;
VectorSubtract( maxs, mins, size );
float maxSize = MAX( size.x, size.y );
maxSize = MAX( maxSize, size.z );
 
// Figure out the texture size
// For now, we're going to assume a fixed number of shadow texels
// per shadow-caster size; add in some extra space at the boundary.
int texelCount = TEXEL_SIZE_PER_CASTER_SIZE * maxSize;
// Pick the first power of 2 larger...
int textureSize = 1;
while (textureSize < texelCount)
{
textureSize <<= 1;
}
 
shadow.m_ShadowTexture = m_ShadowAllocator.AllocateTexture( textureSize, textureSize );
}
 
 
void CClientShadowMgr::CleanUpRenderToTextureShadow( ClientShadowHandle_t h )
{
{
ClientShadow_t& shadow = m_Shadows[h];
Assert( m_Shadows.IsValidIndex(handle) );
if (m_RenderToTextureActive && (shadow.m_Flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE))
RemoveShadowFromDirtyList( handle );
{
shadowmgr->DestroyShadow( m_Shadows[handle].m_ShadowHandle );
m_ShadowAllocator.DeallocateTexture( shadow.m_ShadowTexture );
ClientLeafSystem()->RemoveShadow( m_Shadows[handle].m_ClientLeafShadowHandle );
shadow.m_ShadowTexture = INVALID_TEXTURE_HANDLE;
CleanUpRenderToTextureShadow( handle );
}
m_Shadows.Remove(handle);
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Causes all shadows to be re-updated
// Build the worldtotexture matrix
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateAllShadows()
void CClientShadowMgr::BuildGeneralWorldToShadowMatrix( VMatrix& matWorldToShadow,
const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec )
{
{
for ( ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
// We're assuming here that xvec + yvec aren't necessary perpendicular
{
ClientShadow_t& shadow = m_Shadows[i];


// Don't bother with flashlights
// The shadow->world matrix is pretty simple:
if ( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) != 0 )
// Just stick the origin in the translation component
continue;
// and the vectors in the columns...
matWorldToShadow.SetBasisVectors( xvec, yvec, dir );
matWorldToShadow.SetTranslation( origin );
matWorldToShadow[3][0] = matWorldToShadow[3][1] = matWorldToShadow[3][2] = 0.0f;
matWorldToShadow[3][3] = 1.0f;


IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
// Now do a general inverse to get matWorldToShadow
if ( !pRenderable )
MatrixInverseGeneral( matWorldToShadow, matWorldToShadow );
continue;
 
Assert( pRenderable->GetShadowHandle() == i );
AddToDirtyShadowList( pRenderable, true );
}
}
}


void CClientShadowMgr::BuildWorldToShadowMatrix( VMatrix& matWorldToShadow, const Vector& origin, const Quaternion& quatOrientation )
{
// The shadow->world matrix is pretty simple:
// Just stick the origin in the translation component
// and the vectors in the columns...
// The inverse of this transposes the rotational component
// and the translational component =  - (rotation transpose) * origin


//-----------------------------------------------------------------------------
matrix3x4_t matOrientation;
// Sets the shadow direction
QuaternionMatrix( quatOrientation, matOrientation ); // Convert quat to matrix3x4
//-----------------------------------------------------------------------------
PositionMatrix( vec3_origin, matOrientation ); // Zero out translation elements
void CClientShadowMgr::SetShadowDirection( const Vector& dir )
{
VectorCopy( dir, m_SimpleShadowDir );
VectorNormalize( m_SimpleShadowDir );


if ( m_RenderToTextureActive )
VMatrix matBasis( matOrientation ); // Convert matrix3x4 to VMatrix
{
 
UpdateAllShadows();
Vector vForward, vLeft, vUp;
}
matBasis.GetBasisVectors( vForward, vLeft, vUp );
matBasis.SetForward( vLeft ); // Bizarre vector flip inherited from earlier code, WTF?
matBasis.SetLeft( vUp );
matBasis.SetUp( vForward );
matWorldToShadow = matBasis.Transpose(); // Transpose
 
Vector translation;
Vector3DMultiply( matWorldToShadow, origin, translation );
 
translation *= -1.0f;
matWorldToShadow.SetTranslation( translation );
 
// The the bottom row.
matWorldToShadow[3][0] = matWorldToShadow[3][1] = matWorldToShadow[3][2] = 0.0f;
matWorldToShadow[3][3] = 1.0f;
}
}


const Vector &CClientShadowMgr::GetShadowDirection() const
void CClientShadowMgr::BuildPerspectiveWorldToFlashlightMatrix( VMatrix& matWorldToShadow, const FlashlightState_t &flashlightState )
{
{
// This will cause blobby shadows to always project straight down
VPROF_BUDGET( "CClientShadowMgr::BuildPerspectiveWorldToFlashlightMatrix", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
static Vector s_vecDown( 0, 0, -1 );
 
if ( !m_RenderToTextureActive )
// Buildworld to shadow matrix, then perspective projection and concatenate
return s_vecDown;
VMatrix matWorldToShadowView, matPerspective;
BuildWorldToShadowMatrix( matWorldToShadowView, flashlightState.m_vecLightOrigin,
  flashlightState.m_quatOrientation );
 
MatrixBuildPerspective( matPerspective, flashlightState.m_fHorizontalFOVDegrees,
flashlightState.m_fVerticalFOVDegrees,
flashlightState.m_NearZ, flashlightState.m_FarZ );


return m_SimpleShadowDir;
MatrixMultiply( matPerspective, matWorldToShadowView, matWorldToShadow );
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Gets shadow information for a particular renderable
// Compute the shadow origin and attenuation start distance
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
float CClientShadowMgr::GetShadowDistance( IClientRenderable *pRenderable ) const
float CClientShadowMgr::ComputeLocalShadowOrigin( IClientRenderable* pRenderable,
const Vector& mins, const Vector& maxs, const Vector& localShadowDir, float backupFactor, Vector& origin )
{
{
float flDist = m_flShadowCastDist;
// Compute the centroid of the object...
Vector vecCentroid;
VectorAdd( mins, maxs, vecCentroid );
vecCentroid *= 0.5f;


// Allow the renderable to override the default
Vector vecSize;
pRenderable->GetShadowCastDistance( &flDist, GetActualShadowCastType( pRenderable ) );
VectorSubtract( maxs, mins, vecSize );
float flRadius = vecSize.Length() * 0.5f;


return flDist;
// NOTE: The *origin* of the shadow cast is a point on a line passing through
}
// the centroid of the caster. The direction of this line is the shadow cast direction,
// and the point on that line corresponds to the endpoint of the box that is
// furthest *back* along the shadow direction


const Vector &CClientShadowMgr::GetShadowDirection( IClientRenderable *pRenderable ) const
// For the first point at which the shadow could possibly start falling off,
{
// we need to use the point at which the ray described above leaves the
Vector &vecResult = AllocTempVector();
// bounding sphere surrounding the entity. This is necessary because otherwise,
vecResult = GetShadowDirection();
// tall, thin objects would have their shadows appear + disappear as then spun about their origin


// Allow the renderable to override the default
// Figure out the corner corresponding to the min + max projection
pRenderable->GetShadowCastDirection( &vecResult, GetActualShadowCastType( pRenderable ) );
// along the shadow direction


return vecResult;
// We're basically finding the point on the cube that has the largest and smallest
}
// dot product with the local shadow dir. Then we're taking the dot product
 
// of that with the localShadowDir. lastly, we're subtracting out the
 
// centroid projection to give us a distance along the localShadowDir to
//-----------------------------------------------------------------------------
// the front and back of the cube along the direction of the ray.
// Sets the shadow distance
float centroidProjection = DotProduct( vecCentroid, localShadowDir );
//-----------------------------------------------------------------------------
float minDist = -centroidProjection;
void CClientShadowMgr::SetShadowDistance( float flMaxDistance )
for (int i = 0; i < 3; ++i)
{
{
m_flShadowCastDist = flMaxDistance;
if ( localShadowDir[i] > 0.0f )
UpdateAllShadows();
{
}
minDist += localShadowDir[i] * mins[i];
}
else
{
minDist += localShadowDir[i] * maxs[i];
}
}


float CClientShadowMgr::GetShadowDistance( ) const
minDist *= backupFactor;
{
return m_flShadowCastDist;
}


VectorMA( vecCentroid, minDist, localShadowDir, origin );


//-----------------------------------------------------------------------------
return flRadius - minDist;
// Sets the screen area at which blobby shadows are always used
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetShadowBlobbyCutoffArea( float flMinArea )
{
m_flMinShadowArea = flMinArea;
}
 
float CClientShadowMgr::GetBlobbyCutoffArea( ) const
{
return m_flMinShadowArea;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose:
// Sorts the components of a vector
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetFalloffBias( ClientShadowHandle_t handle, unsigned char ucBias )
static inline void SortAbsVectorComponents( const Vector& src, int* pVecIdx )
{
{
shadowmgr->SetFalloffBias( m_Shadows[handle].m_ShadowHandle, ucBias );
Vector absVec( fabs(src[0]), fabs(src[1]), fabs(src[2]) );
}


//-----------------------------------------------------------------------------
int maxIdx = (absVec[0] > absVec[1]) ? 0 : 1;
// Returns the shadow texture
if (absVec[2] > absVec[maxIdx])
//-----------------------------------------------------------------------------
{
ITexture* CClientShadowMgr::GetShadowTexture( unsigned short h )
maxIdx = 2;
{
}
return m_ShadowAllocator.GetTexture();
 
// always choose something right-handed....
switch( maxIdx )
{
case 0:
pVecIdx[0] = 1;
pVecIdx[1] = 2;
pVecIdx[2] = 0;
break;
case 1:
pVecIdx[0] = 2;
pVecIdx[1] = 0;
pVecIdx[2] = 1;
break;
case 2:
pVecIdx[0] = 0;
pVecIdx[1] = 1;
pVecIdx[2] = 2;
break;
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Returns information needed by the model proxy
// Build the worldtotexture matrix
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
const ShadowInfo_t& CClientShadowMgr::GetShadowInfo( ClientShadowHandle_t h )
static void BuildWorldToTextureMatrix( const VMatrix& matWorldToShadow,
const Vector2D& size, VMatrix& matWorldToTexture )
{
{
return shadowmgr->GetInfo( m_Shadows[h].m_ShadowHandle );
// Build a matrix that maps from shadow space to (u,v) coordinates
VMatrix shadowToUnit;
MatrixBuildScale( shadowToUnit, 1.0f / size.x, 1.0f / size.y, 1.0f );
shadowToUnit[0][3] = shadowToUnit[1][3] = 0.5f;
 
// Store off the world to (u,v) transformation
MatrixMultiply( shadowToUnit, matWorldToShadow, matWorldToTexture );
}
}


static void BuildOrthoWorldToShadowMatrix( VMatrix& worldToShadow,
const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec )
{
// This version is faster and assumes dir, xvec, yvec are perpendicular
AssertFloatEquals( DotProduct( dir, xvec ), 0.0f, 1e-3 );
AssertFloatEquals( DotProduct( dir, yvec ), 0.0f, 1e-3 );
AssertFloatEquals( DotProduct( xvec, yvec ), 0.0f, 1e-3 );


//-----------------------------------------------------------------------------
// The shadow->world matrix is pretty simple:
// Renders the shadow texture to screen...
// Just stick the origin in the translation component
//-----------------------------------------------------------------------------
// and the vectors in the columns...
void CClientShadowMgr::RenderShadowTexture( int w, int h )
// The inverse of this transposes the rotational component
{
// and the translational component =  - (rotation transpose) * origin
if (m_RenderToTextureActive)
worldToShadow.SetBasisVectors( xvec, yvec, dir );
{
MatrixTranspose( worldToShadow, worldToShadow );
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->Bind( m_RenderShadow );
IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
 
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );


meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
Vector translation;
meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
Vector3DMultiply( worldToShadow, origin, translation );
meshBuilder.Color4ub( 0, 0, 0, 0 );
meshBuilder.AdvanceVertex();


meshBuilder.Position3f( w, 0.0f, 0.0f );
translation *= -1.0f;
meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
worldToShadow.SetTranslation( translation );
meshBuilder.Color4ub( 0, 0, 0, 0 );
meshBuilder.AdvanceVertex();


meshBuilder.Position3f( w, h, 0.0f );
// The the bottom row.
meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
worldToShadow[3][0] = worldToShadow[3][1] = worldToShadow[3][2] = 0.0f;
meshBuilder.Color4ub( 0, 0, 0, 0 );
worldToShadow[3][3] = 1.0f;
meshBuilder.AdvanceVertex();
}


meshBuilder.Position3f( 0.0f, h, 0.0f );
//-----------------------------------------------------------------------------
meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
// Set extra clip planes related to shadows...
meshBuilder.Color4ub( 0, 0, 0, 0 );
//-----------------------------------------------------------------------------
meshBuilder.AdvanceVertex();
void CClientShadowMgr::ClearExtraClipPlanes( ClientShadowHandle_t h )
{
shadowmgr->ClearExtraClipPlanes( m_Shadows[h].m_ShadowHandle );
}


meshBuilder.End();
void CClientShadowMgr::AddExtraClipPlane( ClientShadowHandle_t h, const Vector& normal, float dist )
pMesh->Draw();
{
}
shadowmgr->AddExtraClipPlane( m_Shadows[h].m_ShadowHandle, normal, dist );
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Create/destroy a shadow
// Compute the extra shadow planes
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
ClientShadowHandle_t CClientShadowMgr::CreateProjectedTexture( ClientEntityHandle_t entity, int flags )
void CClientShadowMgr::ComputeExtraClipPlanes( IClientRenderable* pRenderable,
ClientShadowHandle_t handle, const Vector* vec,
const Vector& mins, const Vector& maxs, const Vector& localShadowDir )
{
{
// We need to know if it's a brush model for shadows
// Compute the world-space position of the corner of the bounding box
if( !( flags & SHADOW_FLAGS_FLASHLIGHT ) )
// that's got the highest dotproduct with the local shadow dir...
Vector origin = pRenderable->GetRenderOrigin( );
float dir[3];
 
int i;
for ( i = 0; i < 3; ++i )
{
{
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( entity );
if (localShadowDir[i] < 0.0f)
if ( !pRenderable )
return m_Shadows.InvalidIndex();
 
int modelType = modelinfo->GetModelType( pRenderable->GetModel() );
if (modelType == mod_brush)
{
{
flags |= SHADOW_FLAGS_BRUSH_MODEL;
VectorMA( origin, maxs[i], vec[i], origin );
dir[i] = 1;
}
}
}
else
{
VectorMA( origin, mins[i], vec[i], origin );
dir[i] = -1;
}
}


ClientShadowHandle_t h = m_Shadows.AddToTail();
// Now that we have it, create 3 planes...
ClientShadow_t& shadow = m_Shadows[h];
Vector normal;
shadow.m_Entity = entity;
ClearExtraClipPlanes(handle);
shadow.m_ClientLeafShadowHandle = ClientLeafSystem()->AddShadow( h, flags );
for ( i = 0; i < 3; ++i )
shadow.m_Flags = flags;
shadow.m_nRenderFrame = -1;
shadow.m_ShadowDir = GetShadowDirection();
    shadow.m_CurrentLightPos.Init( FLT_MAX, FLT_MAX, FLT_MAX );
    shadow.m_TargetLightPos.Init( FLT_MAX, FLT_MAX, FLT_MAX );
    shadow.m_LightPosLerp = FLT_MAX;
shadow.m_LastOrigin.Init( FLT_MAX, FLT_MAX, FLT_MAX );
shadow.m_LastAngles.Init( FLT_MAX, FLT_MAX, FLT_MAX );
Assert( ( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 ) !=
( ( shadow.m_Flags & SHADOW_FLAGS_SHADOW ) == 0 ) );
 
// Set up the flags....
IMaterial* pShadowMaterial = m_SimpleShadow;
IMaterial* pShadowModelMaterial = m_SimpleShadow;
void* pShadowProxyData = (void*)CLIENTSHADOW_INVALID_HANDLE;
 
if ( m_RenderToTextureActive && (flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE) )
{
{
SetupRenderToTextureShadow(h);
VectorMultiply( vec[i], dir[i], normal );
 
float dist = DotProduct( normal, origin );
pShadowMaterial = m_RenderShadow;
AddExtraClipPlane( handle, normal, dist );
pShadowModelMaterial = m_RenderModelShadow;
pShadowProxyData = (void*)(uintp)h;
}
}


if( flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE )
ClientShadow_t& shadow = m_Shadows[handle];
C_BaseEntity *pEntity = ClientEntityList().GetBaseEntityFromHandle( shadow.m_Entity );
if ( pEntity && pEntity->m_bEnableRenderingClipPlane )
{
{
pShadowMaterial = m_RenderShadow;
normal[ 0 ] = -pEntity->m_fRenderingClipPlane[ 0 ];
pShadowModelMaterial = m_RenderModelShadow;
normal[ 1 ] = -pEntity->m_fRenderingClipPlane[ 1 ];
pShadowProxyData = (void*)(uintp)h;
normal[ 2 ] = -pEntity->m_fRenderingClipPlane[ 2 ];
AddExtraClipPlane( handle, normal, -pEntity->m_fRenderingClipPlane[ 3 ] - 0.5f );
}
}
}


int createShadowFlags;
inline ShadowType_t CClientShadowMgr::GetActualShadowCastType( ClientShadowHandle_t handle ) const
if( flags & SHADOW_FLAGS_FLASHLIGHT )
{
if ( handle == CLIENTSHADOW_INVALID_HANDLE )
{
return SHADOWS_NONE;
}
if ( m_Shadows[handle].m_Flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE )
{
return ( m_RenderToTextureActive ? SHADOWS_RENDER_TO_TEXTURE : SHADOWS_SIMPLE );
}
else if( m_Shadows[handle].m_Flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE )
{
{
// don't use SHADOW_CACHE_VERTS with projective lightsources since we expect that they will change every frame.
return SHADOWS_RENDER_TO_DEPTH_TEXTURE;
// FIXME: might want to make it cache optionally if it's an entity light that is static.
createShadowFlags = SHADOW_FLASHLIGHT;
}
}
else
else
{
{
createShadowFlags = SHADOW_CACHE_VERTS;
return SHADOWS_SIMPLE;
}
}
shadow.m_ShadowHandle = shadowmgr->CreateShadowEx( pShadowMaterial, pShadowModelMaterial, pShadowProxyData, createShadowFlags );
return h;
}
}


ClientShadowHandle_t CClientShadowMgr::CreateFlashlight( const FlashlightState_t &lightState )
inline ShadowType_t CClientShadowMgr::GetActualShadowCastType( IClientRenderable *pEnt ) const
{
{
// We don't really need a model entity handle for a projective light source, so use an invalid one.
return GetActualShadowCastType( pEnt->GetShadowHandle() );
static ClientEntityHandle_t invalidHandle = INVALID_CLIENTENTITY_HANDLE;
}


int shadowFlags = SHADOW_FLAGS_FLASHLIGHT | SHADOW_FLAGS_LIGHT_WORLD;
//-----------------------------------------------------------------------------
if( lightState.m_bEnableShadows && r_flashlightdepthtexture.GetBool() )
// Adds a shadow to all leaves along a ray
//-----------------------------------------------------------------------------
class CShadowLeafEnum : public ISpatialLeafEnumerator
{
public:
bool EnumerateLeaf( int leaf, int context )
{
{
shadowFlags |= SHADOW_FLAGS_USE_DEPTH_TEXTURE;
m_LeafList.AddToTail( leaf );
return true;
}
}


ClientShadowHandle_t shadowHandle = CreateProjectedTexture( invalidHandle, shadowFlags );
CUtlVectorFixedGrowable< int, 512 > m_LeafList;
};


UpdateFlashlightState( shadowHandle, lightState );
//-----------------------------------------------------------------------------
UpdateProjectedTexture( shadowHandle, true );
// Builds a list of leaves inside the shadow volume
return shadowHandle;
//-----------------------------------------------------------------------------
}
static void BuildShadowLeafList( CShadowLeafEnum *pEnum, const Vector& origin,  
const Vector& dir, const Vector2D& size, float maxDist )
ClientShadowHandle_t CClientShadowMgr::CreateShadow( ClientEntityHandle_t entity, int flags )
{
{
// We don't really need a model entity handle for a projective light source, so use an invalid one.
Ray_t ray;
flags &= ~SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK;
VectorCopy( origin, ray.m_Start );
flags |= SHADOW_FLAGS_SHADOW | SHADOW_FLAGS_TEXTURE_DIRTY;
VectorMultiply( dir, maxDist, ray.m_Delta );
ClientShadowHandle_t shadowHandle = CreateProjectedTexture( entity, flags );
ray.m_StartOffset.Init( 0, 0, 0 );


IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( entity );
float flRadius = sqrt( size.x * size.x + size.y * size.y ) * 0.5f;
if ( pRenderable )
ray.m_Extents.Init( flRadius, flRadius, flRadius );
{
ray.m_IsRay = false;
Assert( !pRenderable->IsShadowDirty( ) );
ray.m_IsSwept = true;
pRenderable->MarkShadowDirty( true );
}


// NOTE: We *have* to call the version that takes a shadow handle
ISpatialQuery* pQuery = engine->GetBSPTreeQuery();
// even if we have an entity because this entity hasn't set its shadow handle yet
pQuery->EnumerateLeavesAlongRay( ray, pEnum, 0 );
AddToDirtyShadowList( shadowHandle, true );
return shadowHandle;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Updates the flashlight direction and re-computes surfaces it should lie on
// Builds a simple blobby shadow
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateFlashlightState( ClientShadowHandle_t shadowHandle, const FlashlightState_t &flashlightState )
void CClientShadowMgr::BuildOrthoShadow( IClientRenderable* pRenderable,
ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs)
{
{
VPROF_BUDGET( "CClientShadowMgr::UpdateFlashlightState", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
// Get the object's basis
Vector vec[3];
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
vec[1] *= -1.0f;


BuildPerspectiveWorldToFlashlightMatrix( m_Shadows[shadowHandle].m_WorldToShadow, flashlightState );
Vector vecShadowDir = GetShadowDirection( handle );
shadowmgr->UpdateFlashlightState( m_Shadows[shadowHandle].m_ShadowHandle, flashlightState );
}


void CClientShadowMgr::DestroyFlashlight( ClientShadowHandle_t shadowHandle )
// Project the shadow casting direction into the space of the object
{
Vector localShadowDir;
DestroyShadow( shadowHandle );
localShadowDir[0] = DotProduct( vec[0], vecShadowDir );
}
localShadowDir[1] = DotProduct( vec[1], vecShadowDir );
localShadowDir[2] = DotProduct( vec[2], vecShadowDir );


//-----------------------------------------------------------------------------
// Figure out which vector has the largest component perpendicular
// Remove a shadow from the dirty list
// to the shadow handle...
//-----------------------------------------------------------------------------
// Sort by how perpendicular it is
void CClientShadowMgr::RemoveShadowFromDirtyList( ClientShadowHandle_t handle )
int vecIdx[3];
{
SortAbsVectorComponents( localShadowDir, vecIdx );
int idx = m_DirtyShadows.Find( handle );
if ( idx != m_DirtyShadows.InvalidIndex() )
{
// Clean up the shadow update bit.
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[handle].m_Entity );
if ( pRenderable )
{
pRenderable->MarkShadowDirty( false );
}
m_DirtyShadows.RemoveAt( idx );
}
}


// Here's our shadow basis vectors; namely the ones that are
// most perpendicular to the shadow casting direction
Vector xvec = vec[vecIdx[0]];
Vector yvec = vec[vecIdx[1]];


//-----------------------------------------------------------------------------
// Project them into a plane perpendicular to the shadow direction
// Remove a shadow
xvec -= vecShadowDir * DotProduct( vecShadowDir, xvec );
//-----------------------------------------------------------------------------
yvec -= vecShadowDir * DotProduct( vecShadowDir, yvec );
void CClientShadowMgr::DestroyShadow( ClientShadowHandle_t handle )
VectorNormalize( xvec );
{
VectorNormalize( yvec );
Assert( m_Shadows.IsValidIndex(handle) );
RemoveShadowFromDirtyList( handle );
shadowmgr->DestroyShadow( m_Shadows[handle].m_ShadowHandle );
ClientLeafSystem()->RemoveShadow( m_Shadows[handle].m_ClientLeafShadowHandle );
CleanUpRenderToTextureShadow( handle );
m_Shadows.Remove(handle);
}
 


//-----------------------------------------------------------------------------
// Compute the box size
// Build the worldtotexture matrix
Vector boxSize;
//-----------------------------------------------------------------------------
VectorSubtract( maxs, mins, boxSize );
void CClientShadowMgr::BuildGeneralWorldToShadowMatrix( VMatrix& matWorldToShadow,
const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec )
{
// We're assuming here that xvec + yvec aren't necessary perpendicular


// The shadow->world matrix is pretty simple:
// We project the two longest sides into the vectors perpendicular
// Just stick the origin in the translation component
// to the projection direction, then add in the projection of the perp direction
// and the vectors in the columns...
Vector2D size( boxSize[vecIdx[0]], boxSize[vecIdx[1]] );
matWorldToShadow.SetBasisVectors( xvec, yvec, dir );
size.x *= fabs( DotProduct( vec[vecIdx[0]], xvec ) );
matWorldToShadow.SetTranslation( origin );
size.y *= fabs( DotProduct( vec[vecIdx[1]], yvec ) );
matWorldToShadow[3][0] = matWorldToShadow[3][1] = matWorldToShadow[3][2] = 0.0f;
matWorldToShadow[3][3] = 1.0f;


// Now do a general inverse to get matWorldToShadow
// Add the third component into x and y
MatrixInverseGeneral( matWorldToShadow, matWorldToShadow );
size.x += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], xvec ) );
}
size.y += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], yvec ) );


void CClientShadowMgr::BuildWorldToShadowMatrix( VMatrix& matWorldToShadow, const Vector& origin, const Quaternion& quatOrientation )
// Bloat a bit, since the shadow wants to extend outside the model a bit
{
size.x += 10.0f;
// The shadow->world matrix is pretty simple:
size.y += 10.0f;
// Just stick the origin in the translation component
// and the vectors in the columns...
// The inverse of this transposes the rotational component
// and the translational component = - (rotation transpose) * origin


matrix3x4_t matOrientation;
// Clamp the minimum size
QuaternionMatrix( quatOrientation, matOrientation ); // Convert quat to matrix3x4
Vector2DMax( size, Vector2D(10.0f, 10.0f), size );
PositionMatrix( vec3_origin, matOrientation ); // Zero out translation elements


VMatrix matBasis( matOrientation ); // Convert matrix3x4 to VMatrix
// Place the origin at the point with min dot product with shadow dir
Vector org;
float falloffStart = ComputeLocalShadowOrigin( pRenderable, mins, maxs, localShadowDir, 2.0f, org );


Vector vForward, vLeft, vUp;
// Transform the local origin into world coordinates
matBasis.GetBasisVectors( vForward, vLeft, vUp );
Vector worldOrigin = pRenderable->GetRenderOrigin( );
matBasis.SetForward( vLeft ); // Bizarre vector flip inherited from earlier code, WTF?
VectorMA( worldOrigin, org.x, vec[0], worldOrigin );
matBasis.SetLeft( vUp );
VectorMA( worldOrigin, org.y, vec[1], worldOrigin );
matBasis.SetUp( vForward );
VectorMA( worldOrigin, org.z, vec[2], worldOrigin );
matWorldToShadow = matBasis.Transpose(); // Transpose


Vector translation;
// FUNKY: A trick to reduce annoying texelization artifacts!?
Vector3DMultiply( matWorldToShadow, origin, translation );
float dx = 1.0f / TEXEL_SIZE_PER_CASTER_SIZE;
worldOrigin.x = (int)(worldOrigin.x / dx) * dx;
worldOrigin.y = (int)(worldOrigin.y / dx) * dx;
worldOrigin.z = (int)(worldOrigin.z / dx) * dx;


translation *= -1.0f;
// NOTE: We gotta use the general matrix because xvec and yvec aren't perp
matWorldToShadow.SetTranslation( translation );
VMatrix matWorldToShadow, matWorldToTexture;
BuildGeneralWorldToShadowMatrix( m_Shadows[handle].m_WorldToShadow, worldOrigin, vecShadowDir, xvec, yvec );
BuildWorldToTextureMatrix( m_Shadows[handle].m_WorldToShadow, size, matWorldToTexture );
Vector2DCopy( size, m_Shadows[handle].m_WorldSize );
// Compute the falloff attenuation
// Area computation isn't exact since xvec is not perp to yvec, but close enough
// float shadowArea = size.x * size.y;


// The the bottom row.
// The entity may be overriding our shadow cast distance
matWorldToShadow[3][0] = matWorldToShadow[3][1] = matWorldToShadow[3][2] = 0.0f;
float flShadowCastDistance = GetShadowDistance( pRenderable );
matWorldToShadow[3][3] = 1.0f;
float maxHeight = flShadowCastDistance + falloffStart; //3.0f * sqrt( shadowArea );
}


void CClientShadowMgr::BuildPerspectiveWorldToFlashlightMatrix( VMatrix& matWorldToShadow, const FlashlightState_t &flashlightState )
CShadowLeafEnum leafList;
{
BuildShadowLeafList( &leafList, worldOrigin, vecShadowDir, size, maxHeight );
VPROF_BUDGET( "CClientShadowMgr::BuildPerspectiveWorldToFlashlightMatrix", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
int nCount = leafList.m_LeafList.Count();
const int *pLeafList = leafList.m_LeafList.Base();


// Buildworld to shadow matrix, then perspective projection and concatenate
shadowmgr->ProjectShadow( m_Shadows[handle].m_ShadowHandle, worldOrigin,
VMatrix matWorldToShadowView, matPerspective;
vecShadowDir, matWorldToTexture, size, nCount, pLeafList, maxHeight, falloffStart, MAX_FALLOFF_AMOUNT, pRenderable->GetRenderOrigin() );
BuildWorldToShadowMatrix( matWorldToShadowView, flashlightState.m_vecLightOrigin,
  flashlightState.m_quatOrientation );


MatrixBuildPerspective( matPerspective, flashlightState.m_fHorizontalFOVDegrees,
// Compute extra clip planes to prevent poke-thru
flashlightState.m_fVerticalFOVDegrees,
// FIXME!!!!!!!!!!!!!!  Removing this for now since it seems to mess up the blobby shadows.
flashlightState.m_NearZ, flashlightState.m_FarZ );
// ComputeExtraClipPlanes( pEnt, handle, vec, mins, maxs, localShadowDir );


MatrixMultiply( matPerspective, matWorldToShadowView, matWorldToShadow );
// Add the shadow to the client leaf system so it correctly marks
// leafs as being affected by a particular shadow
ClientLeafSystem()->ProjectShadow( m_Shadows[handle].m_ClientLeafShadowHandle, nCount, pLeafList );
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Compute the shadow origin and attenuation start distance
// Visualization....
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
float CClientShadowMgr::ComputeLocalShadowOrigin( IClientRenderable* pRenderable,  
void CClientShadowMgr::DrawRenderToTextureDebugInfo( IClientRenderable* pRenderable, const Vector& mins, const Vector& maxs )
const Vector& mins, const Vector& maxs, const Vector& localShadowDir, float backupFactor, Vector& origin )
{  
{
// Get the object's basis
// Compute the centroid of the object...
Vector vec[3];
Vector vecCentroid;
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
VectorAdd( mins, maxs, vecCentroid );
vec[1] *= -1.0f;
vecCentroid *= 0.5f;


Vector vecSize;
Vector vecSize;
VectorSubtract( maxs, mins, vecSize );
VectorSubtract( maxs, mins, vecSize );
float flRadius = vecSize.Length() * 0.5f;


// NOTE: The *origin* of the shadow cast is a point on a line passing through
Vector vecOrigin = pRenderable->GetRenderOrigin();
// the centroid of the caster. The direction of this line is the shadow cast direction,
Vector start, end, end2;
// and the point on that line corresponds to the endpoint of the box that is
 
// furthest *back* along the shadow direction
VectorMA( vecOrigin, mins.x, vec[0], start );
VectorMA( start, mins.y, vec[1], start );
VectorMA( start, mins.z, vec[2], start );


// For the first point at which the shadow could possibly start falling off,
VectorMA( start, vecSize.x, vec[0], end );
// we need to use the point at which the ray described above leaves the
VectorMA( end, vecSize.z, vec[2], end2 );
// bounding sphere surrounding the entity. This is necessary because otherwise,
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );
// tall, thin objects would have their shadows appear + disappear as then spun about their origin
debugoverlay->AddLineOverlay( end2, end, 255, 0, 0, true, 0.01 );


// Figure out the corner corresponding to the min + max projection
VectorMA( start, vecSize.y, vec[1], end );
// along the shadow direction
VectorMA( end, vecSize.z, vec[2], end2 );
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );
debugoverlay->AddLineOverlay( end2, end, 255, 0, 0, true, 0.01 );


// We're basically finding the point on the cube that has the largest and smallest
VectorMA( start, vecSize.z, vec[2], end );
// dot product with the local shadow dir. Then we're taking the dot product
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );
// of that with the localShadowDir. lastly, we're subtracting out the
// centroid projection to give us a distance along the localShadowDir to
start = end;
// the front and back of the cube along the direction of the ray.
VectorMA( start, vecSize.x, vec[0], end );
float centroidProjection = DotProduct( vecCentroid, localShadowDir );
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );
float minDist = -centroidProjection;
 
for (int i = 0; i < 3; ++i)
VectorMA( start, vecSize.y, vec[1], end );
{
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );
if ( localShadowDir[i] > 0.0f )
{
minDist += localShadowDir[i] * mins[i];
}
else
{
minDist += localShadowDir[i] * maxs[i];
}
}


minDist *= backupFactor;
VectorMA( end, vecSize.x, vec[0], start );
VectorMA( start, -vecSize.x, vec[0], end );
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );


VectorMA( vecCentroid, minDist, localShadowDir, origin );
VectorMA( start, -vecSize.y, vec[1], end );
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );


return flRadius - minDist;
VectorMA( start, -vecSize.z, vec[2], end );
}
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );


start = end;
VectorMA( start, -vecSize.x, vec[0], end );
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );


//-----------------------------------------------------------------------------
VectorMA( start, -vecSize.y, vec[1], end );
// Sorts the components of a vector
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );
//-----------------------------------------------------------------------------
static inline void SortAbsVectorComponents( const Vector& src, int* pVecIdx )
{
Vector absVec( fabs(src[0]), fabs(src[1]), fabs(src[2]) );


int maxIdx = (absVec[0] > absVec[1]) ? 0 : 1;
C_BaseEntity *pEnt = pRenderable->GetIClientUnknown()->GetBaseEntity();
if (absVec[2] > absVec[maxIdx])
if ( pEnt )
{
{
maxIdx = 2;
debugoverlay->AddTextOverlay( vecOrigin, 0, "%d", pEnt->entindex() );
}
}
 
else
// always choose something right-handed....
switch( maxIdx )
{
{
case 0:
debugoverlay->AddTextOverlay( vecOrigin, 0, "%X", (size_t)pRenderable );
pVecIdx[0] = 1;
pVecIdx[1] = 2;
pVecIdx[2] = 0;
break;
case 1:
pVecIdx[0] = 2;
pVecIdx[1] = 0;
pVecIdx[2] = 1;
break;
case 2:
pVecIdx[0] = 0;
pVecIdx[1] = 1;
pVecIdx[2] = 2;
break;
}
}
}
}


extern ConVar cl_drawshadowtexture;
extern ConVar cl_shadowtextureoverlaysize;


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Build the worldtotexture matrix
// Builds a more complex shadow...
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void BuildWorldToTextureMatrix( const VMatrix& matWorldToShadow,  
void CClientShadowMgr::BuildRenderToTextureShadow( IClientRenderable* pRenderable,  
const Vector2D& size, VMatrix& matWorldToTexture )
ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs)
{
{
// Build a matrix that maps from shadow space to (u,v) coordinates
if ( cl_drawshadowtexture.GetInt() )
VMatrix shadowToUnit;
{
MatrixBuildScale( shadowToUnit, 1.0f / size.x, 1.0f / size.y, 1.0f );
// Red wireframe bounding box around objects whose RTT shadows are being updated that frame
shadowToUnit[0][3] = shadowToUnit[1][3] = 0.5f;
DrawRenderToTextureDebugInfo( pRenderable, mins, maxs );
}


// Store off the world to (u,v) transformation
// Get the object's basis
MatrixMultiply( shadowToUnit, matWorldToShadow, matWorldToTexture );
Vector vec[3];
}
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
vec[1] *= -1.0f;


Vector vecShadowDir = GetShadowDirection( handle );


// Debugging aid
// const model_t *pModel = pRenderable->GetModel();
// const char *pDebugName = modelinfo->GetModelName( pModel );


static void BuildOrthoWorldToShadowMatrix( VMatrix& worldToShadow,
// Project the shadow casting direction into the space of the object
const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec )
Vector localShadowDir;
{
localShadowDir[0] = DotProduct( vec[0], vecShadowDir );
// This version is faster and assumes dir, xvec, yvec are perpendicular
localShadowDir[1] = DotProduct( vec[1], vecShadowDir );
AssertFloatEquals( DotProduct( dir, xvec ), 0.0f, 1e-3 );
localShadowDir[2] = DotProduct( vec[2], vecShadowDir );
AssertFloatEquals( DotProduct( dir, yvec ), 0.0f, 1e-3 );
AssertFloatEquals( DotProduct( xvec, yvec ), 0.0f, 1e-3 );


// The shadow->world matrix is pretty simple:
// Compute the box size
// Just stick the origin in the translation component
Vector boxSize;
// and the vectors in the columns...
VectorSubtract( maxs, mins, boxSize );
// The inverse of this transposes the rotational component
// and the translational component = - (rotation transpose) * origin
Vector yvec;
worldToShadow.SetBasisVectors( xvec, yvec, dir );
float fProjMax = 0.0f;
MatrixTranspose( worldToShadow, worldToShadow );
for( int i = 0; i != 3; ++i )
{
Vector test = vec[i] - ( vecShadowDir * DotProduct( vecShadowDir, vec[i] ) );
test *= boxSize[i]; //doing after the projection to simplify projection math
float fLengthSqr = test.LengthSqr();
if( fLengthSqr > fProjMax )
{
fProjMax = fLengthSqr;
yvec = test;
}
}


Vector translation;
VectorNormalize( yvec );
Vector3DMultiply( worldToShadow, origin, translation );


translation *= -1.0f;
// Compute the x vector
worldToShadow.SetTranslation( translation );
Vector xvec;
CrossProduct( yvec, vecShadowDir, xvec );


// The the bottom row.
// We project the two longest sides into the vectors perpendicular
worldToShadow[3][0] = worldToShadow[3][1] = worldToShadow[3][2] = 0.0f;
// to the projection direction, then add in the projection of the perp direction
worldToShadow[3][3] = 1.0f;
Vector2D size;
}
size.x = boxSize.x * fabs( DotProduct( vec[0], xvec ) ) +
boxSize.y * fabs( DotProduct( vec[1], xvec ) ) +
boxSize.z * fabs( DotProduct( vec[2], xvec ) );
size.y = boxSize.x * fabs( DotProduct( vec[0], yvec ) ) +
boxSize.y * fabs( DotProduct( vec[1], yvec ) ) +
boxSize.z * fabs( DotProduct( vec[2], yvec ) );


size.x += 2.0f * TEXEL_SIZE_PER_CASTER_SIZE;
size.y += 2.0f * TEXEL_SIZE_PER_CASTER_SIZE;


//-----------------------------------------------------------------------------
// Place the origin at the point with min dot product with shadow dir
// Set extra clip planes related to shadows...
Vector org;
//-----------------------------------------------------------------------------
float falloffStart = ComputeLocalShadowOrigin( pRenderable, mins, maxs, localShadowDir, 1.0f, org );
void CClientShadowMgr::ClearExtraClipPlanes( ClientShadowHandle_t h )
{
shadowmgr->ClearExtraClipPlanes( m_Shadows[h].m_ShadowHandle );
}


void CClientShadowMgr::AddExtraClipPlane( ClientShadowHandle_t h, const Vector& normal, float dist )
// Transform the local origin into world coordinates
{
Vector worldOrigin = pRenderable->GetRenderOrigin( );
shadowmgr->AddExtraClipPlane( m_Shadows[h].m_ShadowHandle, normal, dist );
VectorMA( worldOrigin, org.x, vec[0], worldOrigin );
}
VectorMA( worldOrigin, org.y, vec[1], worldOrigin );
VectorMA( worldOrigin, org.z, vec[2], worldOrigin );


VMatrix matWorldToTexture;
BuildOrthoWorldToShadowMatrix( m_Shadows[handle].m_WorldToShadow, worldOrigin, vecShadowDir, xvec, yvec );
BuildWorldToTextureMatrix( m_Shadows[handle].m_WorldToShadow, size, matWorldToTexture );
Vector2DCopy( size, m_Shadows[handle].m_WorldSize );


//-----------------------------------------------------------------------------
// Compute the falloff attenuation
// Compute the extra shadow planes
// Area computation isn't exact since xvec is not perp to yvec, but close enough
//-----------------------------------------------------------------------------
// Extra factor of 4 in the maxHeight due to the size being half as big
void CClientShadowMgr::ComputeExtraClipPlanes( IClientRenderable* pRenderable,
// float shadowArea = size.x * size.y;
ClientShadowHandle_t handle, const Vector* vec,  
const Vector& mins, const Vector& maxs, const Vector& localShadowDir )
{
// Compute the world-space position of the corner of the bounding box
// that's got the highest dotproduct with the local shadow dir...
Vector origin = pRenderable->GetRenderOrigin( );
float dir[3];


int i;
// The entity may be overriding our shadow cast distance
for ( i = 0; i < 3; ++i )
float flShadowCastDistance = GetShadowDistance( pRenderable );
{
float maxHeight = flShadowCastDistance + falloffStart; //3.0f * sqrt( shadowArea );
if (localShadowDir[i] < 0.0f)
{
VectorMA( origin, maxs[i], vec[i], origin );
dir[i] = 1;
}
else
{
VectorMA( origin, mins[i], vec[i], origin );
dir[i] = -1;
}
}


// Now that we have it, create 3 planes...
CShadowLeafEnum leafList;
Vector normal;
BuildShadowLeafList( &leafList, worldOrigin, vecShadowDir, size, maxHeight );
ClearExtraClipPlanes(handle);
int nCount = leafList.m_LeafList.Count();
for ( i = 0; i < 3; ++i )
const int *pLeafList = leafList.m_LeafList.Base();
{
VectorMultiply( vec[i], dir[i], normal );
float dist = DotProduct( normal, origin );
AddExtraClipPlane( handle, normal, dist );
}


ClientShadow_t& shadow = m_Shadows[handle];
shadowmgr->ProjectShadow( m_Shadows[handle].m_ShadowHandle, worldOrigin,
C_BaseEntity *pEntity = ClientEntityList().GetBaseEntityFromHandle( shadow.m_Entity );
vecShadowDir, matWorldToTexture, size, nCount, pLeafList, maxHeight, falloffStart, MAX_FALLOFF_AMOUNT, pRenderable->GetRenderOrigin() );
if ( pEntity && pEntity->m_bEnableRenderingClipPlane )
{
normal[ 0 ] = -pEntity->m_fRenderingClipPlane[ 0 ];
normal[ 1 ] = -pEntity->m_fRenderingClipPlane[ 1 ];
normal[ 2 ] = -pEntity->m_fRenderingClipPlane[ 2 ];
AddExtraClipPlane( handle, normal, -pEntity->m_fRenderingClipPlane[ 3 ] - 0.5f );
}
}


// Compute extra clip planes to prevent poke-thru
ComputeExtraClipPlanes( pRenderable, handle, vec, mins, maxs, localShadowDir );


inline ShadowType_t CClientShadowMgr::GetActualShadowCastType( ClientShadowHandle_t handle ) const
// Add the shadow to the client leaf system so it correctly marks
{
// leafs as being affected by a particular shadow
if ( handle == CLIENTSHADOW_INVALID_HANDLE )
ClientLeafSystem()->ProjectShadow( m_Shadows[handle].m_ClientLeafShadowHandle, nCount, pLeafList );
{
return SHADOWS_NONE;
}
if ( m_Shadows[handle].m_Flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE )
{
return ( m_RenderToTextureActive ? SHADOWS_RENDER_TO_TEXTURE : SHADOWS_SIMPLE );
}
else if( m_Shadows[handle].m_Flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE )
{
return SHADOWS_RENDER_TO_DEPTH_TEXTURE;
}
else
{
return SHADOWS_SIMPLE;
}
}
}


inline ShadowType_t CClientShadowMgr::GetActualShadowCastType( IClientRenderable *pEnt ) const
static void LineDrawHelper( const Vector &startShadowSpace, const Vector &endShadowSpace,
  const VMatrix &shadowToWorld, unsigned char r = 255, unsigned char g = 255,
  unsigned char b = 255 )
{
{
return GetActualShadowCastType( pEnt->GetShadowHandle() );
Vector startWorldSpace, endWorldSpace;
}
Vector3DMultiplyPositionProjective( shadowToWorld, startShadowSpace, startWorldSpace );
Vector3DMultiplyPositionProjective( shadowToWorld, endShadowSpace, endWorldSpace );


debugoverlay->AddLineOverlay( startWorldSpace + Vector( 0.0f, 0.0f, 1.0f ),
endWorldSpace + Vector( 0.0f, 0.0f, 1.0f ), r, g, b, false, -1 );
}


//-----------------------------------------------------------------------------
static void DebugDrawFrustum( const Vector &vOrigin, const VMatrix &matWorldToFlashlight )
// Adds a shadow to all leaves along a ray
//-----------------------------------------------------------------------------
class CShadowLeafEnum : public ISpatialLeafEnumerator
{
{
public:
VMatrix flashlightToWorld;
bool EnumerateLeaf( int leaf, int context )
MatrixInverseGeneral( matWorldToFlashlight, flashlightToWorld );
{
m_LeafList.AddToTail( leaf );
// Draw boundaries of frustum
return true;
LineDrawHelper( Vector( 0.0f, 0.0f, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
}
LineDrawHelper( Vector( 0.0f, 0.0f, 1.0f ), Vector( 0.0f, 1.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
 
LineDrawHelper( Vector( 0.0f, 1.0f, 1.0f ), Vector( 0.0f, 1.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
CUtlVectorFixedGrowable< int, 512 > m_LeafList;
LineDrawHelper( Vector( 0.0f, 1.0f, 0.0f ), Vector( 0.0f, 0.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
};
LineDrawHelper( Vector( 1.0f, 0.0f, 0.0f ), Vector( 1.0f, 0.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 1.0f, 0.0f, 1.0f ), Vector( 1.0f, 1.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 1.0f, 1.0f, 1.0f ), Vector( 1.0f, 1.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 1.0f, 1.0f, 0.0f ), Vector( 1.0f, 0.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 0.0f, 0.0f, 0.0f ), Vector( 1.0f, 0.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 0.0f, 0.0f, 1.0f ), Vector( 1.0f, 0.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 0.0f, 1.0f, 1.0f ), Vector( 1.0f, 1.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 0.0f, 1.0f, 0.0f ), Vector( 1.0f, 1.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );


// Draw RGB triad at front plane
LineDrawHelper( Vector( 0.5f, 0.5f, 0.0f ), Vector( 1.0f, 0.5f, 0.0f ),  flashlightToWorld, 255,  0,  0 );
LineDrawHelper( Vector( 0.5f, 0.5f, 0.0f ), Vector( 0.5f, 1.0f, 0.0f ),  flashlightToWorld,  0, 255,  0 );
LineDrawHelper( Vector( 0.5f, 0.5f, 0.0f ), Vector( 0.5f, 0.5f, 0.35f ), flashlightToWorld,  0,  0, 255 );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Builds a list of leaves inside the shadow volume
// Builds a list of leaves inside the flashlight volume
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void BuildShadowLeafList( CShadowLeafEnum *pEnum, const Vector& origin,
static void BuildFlashlightLeafList( CShadowLeafEnum *pEnum, const VMatrix &worldToShadow )
const Vector& dir, const Vector2D& size, float maxDist )
{
{
Ray_t ray;
// Use an AABB around the frustum to enumerate leaves.
VectorCopy( origin, ray.m_Start );
Vector mins, maxs;
VectorMultiply( dir, maxDist, ray.m_Delta );
CalculateAABBFromProjectionMatrix( worldToShadow, &mins, &maxs );
ray.m_StartOffset.Init( 0, 0, 0 );
 
float flRadius = sqrt( size.x * size.x + size.y * size.y ) * 0.5f;
ray.m_Extents.Init( flRadius, flRadius, flRadius );
ray.m_IsRay = false;
ray.m_IsSwept = true;
 
ISpatialQuery* pQuery = engine->GetBSPTreeQuery();
ISpatialQuery* pQuery = engine->GetBSPTreeQuery();
pQuery->EnumerateLeavesAlongRay( ray, pEnum, 0 );
pQuery->EnumerateLeavesInBox( mins, maxs, pEnum, 0 );
}
}


 
void CClientShadowMgr::BuildFlashlight( ClientShadowHandle_t handle )
//-----------------------------------------------------------------------------
// Builds a simple blobby shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::BuildOrthoShadow( IClientRenderable* pRenderable,
ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs)
{
{
// Get the object's basis
// For the 360, we just draw flashlights with the main geometry
Vector vec[3];
// and bypass the entire shadow casting system.
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
ClientShadow_t &shadow = m_Shadows[handle];
vec[1] *= -1.0f;
if ( IsX360() || r_flashlight_version2.GetInt() )
{
// This will update the matrices, but not do work to add the flashlight to surfaces
shadowmgr->ProjectFlashlight( shadow.m_ShadowHandle, shadow.m_WorldToShadow, 0, NULL );
return;
}


Vector vecShadowDir = GetShadowDirection( handle );
VPROF_BUDGET( "CClientShadowMgr::BuildFlashlight", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );


// Project the shadow casting direction into the space of the object
bool bLightModels = r_flashlightmodels.GetBool();
Vector localShadowDir;
bool bLightSpecificEntity = shadow.m_hTargetEntity.Get() != NULL;
localShadowDir[0] = DotProduct( vec[0], vecShadowDir );
bool bLightWorld = ( shadow.m_Flags & SHADOW_FLAGS_LIGHT_WORLD ) != 0;
localShadowDir[1] = DotProduct( vec[1], vecShadowDir );
int nCount = 0;
localShadowDir[2] = DotProduct( vec[2], vecShadowDir );
const int *pLeafList = 0;


// Figure out which vector has the largest component perpendicular
CShadowLeafEnum leafList;
// to the shadow handle...
if ( bLightWorld || ( bLightModels && !bLightSpecificEntity ) )
// Sort by how perpendicular it is
{
int vecIdx[3];
BuildFlashlightLeafList( &leafList, shadow.m_WorldToShadow );
SortAbsVectorComponents( localShadowDir, vecIdx );
nCount = leafList.m_LeafList.Count();
pLeafList = leafList.m_LeafList.Base();
}


// Here's our shadow basis vectors; namely the ones that are
if( bLightWorld )
// most perpendicular to the shadow casting direction
{
Vector xvec = vec[vecIdx[0]];
shadowmgr->ProjectFlashlight( shadow.m_ShadowHandle, shadow.m_WorldToShadow, nCount, pLeafList );
Vector yvec = vec[vecIdx[1]];
}
else
{
// This should clear all models and surfaces from this shadow
shadowmgr->EnableShadow( shadow.m_ShadowHandle, false );
shadowmgr->EnableShadow( shadow.m_ShadowHandle, true );
}


// Project them into a plane perpendicular to the shadow direction
if ( !bLightModels )
xvec -= vecShadowDir * DotProduct( vecShadowDir, xvec );
return;
yvec -= vecShadowDir * DotProduct( vecShadowDir, yvec );
VectorNormalize( xvec );
VectorNormalize( yvec );


// Compute the box size
if ( !bLightSpecificEntity )
Vector boxSize;
{
VectorSubtract( maxs, mins, boxSize );
// Add the shadow to the client leaf system so it correctly marks
// leafs as being affected by a particular shadow
ClientLeafSystem()->ProjectFlashlight( shadow.m_ClientLeafShadowHandle, nCount, pLeafList );
return;
}


// We project the two longest sides into the vectors perpendicular
// We know what we are focused on, so just add the shadow directly to that receiver
// to the projection direction, then add in the projection of the perp direction
Assert( shadow.m_hTargetEntity->GetModel() );
Vector2D size( boxSize[vecIdx[0]], boxSize[vecIdx[1]] );
size.x *= fabs( DotProduct( vec[vecIdx[0]], xvec ) );
size.y *= fabs( DotProduct( vec[vecIdx[1]], yvec ) );


// Add the third component into x and y
C_BaseEntity *pChild = shadow.m_hTargetEntity->FirstMoveChild();
size.x += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], xvec ) );
while( pChild )
size.y += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], yvec ) );
{
int modelType = modelinfo->GetModelType( pChild->GetModel() );
if (modelType == mod_brush)
{
AddShadowToReceiver( handle, pChild, SHADOW_RECEIVER_BRUSH_MODEL );
}
else if ( modelType == mod_studio )
{
AddShadowToReceiver( handle, pChild, SHADOW_RECEIVER_STUDIO_MODEL );
}


// Bloat a bit, since the shadow wants to extend outside the model a bit
pChild = pChild->NextMovePeer();
size.x += 10.0f;
}
size.y += 10.0f;


// Clamp the minimum size
int modelType = modelinfo->GetModelType( shadow.m_hTargetEntity->GetModel() );
Vector2DMax( size, Vector2D(10.0f, 10.0f), size );
if (modelType == mod_brush)
{
AddShadowToReceiver( handle, shadow.m_hTargetEntity, SHADOW_RECEIVER_BRUSH_MODEL );
}
else if ( modelType == mod_studio )
{
AddShadowToReceiver( handle, shadow.m_hTargetEntity, SHADOW_RECEIVER_STUDIO_MODEL );
}
}


// Place the origin at the point with min dot product with shadow dir
//-----------------------------------------------------------------------------
Vector org;
// Adds the child bounds to the bounding box
float falloffStart = ComputeLocalShadowOrigin( pRenderable, mins, maxs, localShadowDir, 2.0f, org );
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddChildBounds( matrix3x4_t &matWorldToBBox, IClientRenderable* pParent, Vector &vecMins, Vector &vecMaxs )
{
Vector vecChildMins, vecChildMaxs;
Vector vecNewChildMins, vecNewChildMaxs;
matrix3x4_t childToBBox;


// Transform the local origin into world coordinates
IClientRenderable *pChild = pParent->FirstShadowChild();
Vector worldOrigin = pRenderable->GetRenderOrigin( );
while( pChild )
VectorMA( worldOrigin, org.x, vec[0], worldOrigin );
{
VectorMA( worldOrigin, org.y, vec[1], worldOrigin );
// Transform the child bbox into the space of the main bbox
VectorMA( worldOrigin, org.z, vec[2], worldOrigin );
// FIXME: Optimize this?
if ( GetActualShadowCastType( pChild ) != SHADOWS_NONE)
{
pChild->GetShadowRenderBounds( vecChildMins, vecChildMaxs, SHADOWS_RENDER_TO_TEXTURE );
ConcatTransforms( matWorldToBBox, pChild->RenderableToWorldTransform(), childToBBox );
TransformAABB( childToBBox, vecChildMins, vecChildMaxs, vecNewChildMins, vecNewChildMaxs );
VectorMin( vecMins, vecNewChildMins, vecMins );
VectorMax( vecMaxs, vecNewChildMaxs, vecMaxs );
}


// FUNKY: A trick to reduce annoying texelization artifacts!?
AddChildBounds( matWorldToBBox, pChild, vecMins, vecMaxs );
float dx = 1.0f / TEXEL_SIZE_PER_CASTER_SIZE;
pChild = pChild->NextShadowPeer();
worldOrigin.x = (int)(worldOrigin.x / dx) * dx;
}
worldOrigin.y = (int)(worldOrigin.y / dx) * dx;
}
worldOrigin.z = (int)(worldOrigin.z / dx) * dx;


// NOTE: We gotta use the general matrix because xvec and yvec aren't perp
//-----------------------------------------------------------------------------
VMatrix matWorldToShadow, matWorldToTexture;
// Compute a bounds for the entity + children
BuildGeneralWorldToShadowMatrix( m_Shadows[handle].m_WorldToShadow, worldOrigin, vecShadowDir, xvec, yvec );
//-----------------------------------------------------------------------------
BuildWorldToTextureMatrix( m_Shadows[handle].m_WorldToShadow, size, matWorldToTexture );
void CClientShadowMgr::ComputeHierarchicalBounds( IClientRenderable *pRenderable, Vector &vecMins, Vector &vecMaxs )
Vector2DCopy( size, m_Shadows[handle].m_WorldSize );
{
ShadowType_t shadowType = GetActualShadowCastType( pRenderable );
// Compute the falloff attenuation
// Area computation isn't exact since xvec is not perp to yvec, but close enough
// float shadowArea = size.x * size.y;


// The entity may be overriding our shadow cast distance
pRenderable->GetShadowRenderBounds( vecMins, vecMaxs, shadowType );
float flShadowCastDistance = GetShadowDistance( pRenderable );
float maxHeight = flShadowCastDistance + falloffStart; //3.0f * sqrt( shadowArea );


CShadowLeafEnum leafList;
// We could use a good solution for this in the regular PC build, since
BuildShadowLeafList( &leafList, worldOrigin, vecShadowDir, size, maxHeight );
// it causes lots of extra bone setups for entities you can't see.
int nCount = leafList.m_LeafList.Count();
if ( IsPC() )
const int *pLeafList = leafList.m_LeafList.Base();
{
IClientRenderable *pChild = pRenderable->FirstShadowChild();


shadowmgr->ProjectShadow( m_Shadows[handle].m_ShadowHandle, worldOrigin,
// Don't recurse down the tree when we hit a blobby shadow
vecShadowDir, matWorldToTexture, size, nCount, pLeafList, maxHeight, falloffStart, MAX_FALLOFF_AMOUNT, pRenderable->GetRenderOrigin() );
if ( pChild && shadowType != SHADOWS_SIMPLE )
 
{
// Compute extra clip planes to prevent poke-thru
matrix3x4_t matWorldToBBox;
// FIXME!!!!!!!!!!!!!!  Removing this for now since it seems to mess up the blobby shadows.
MatrixInvert( pRenderable->RenderableToWorldTransform(), matWorldToBBox );
// ComputeExtraClipPlanes( pEnt, handle, vec, mins, maxs, localShadowDir );
AddChildBounds( matWorldToBBox, pRenderable, vecMins, vecMaxs );
 
}
// Add the shadow to the client leaf system so it correctly marks
}
// leafs as being affected by a particular shadow
ClientLeafSystem()->ProjectShadow( m_Shadows[handle].m_ClientLeafShadowHandle, nCount, pLeafList );
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Visualization....
// Shadow update functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::DrawRenderToTextureDebugInfo( IClientRenderable* pRenderable, const Vector& mins, const Vector& maxs )
void CClientShadowMgr::UpdateStudioShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle )
{  
{
// Get the object's basis
if( !( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) )
Vector vec[3];
{
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
Vector mins, maxs;
vec[1] *= -1.0f;
ComputeHierarchicalBounds( pRenderable, mins, maxs );


Vector vecSize;
ShadowType_t shadowType = GetActualShadowCastType( handle );
VectorSubtract( maxs, mins, vecSize );
if ( shadowType != SHADOWS_RENDER_TO_TEXTURE )
{
BuildOrthoShadow( pRenderable, handle, mins, maxs );
}
else
{
BuildRenderToTextureShadow( pRenderable, handle, mins, maxs );
}
}
else
{
BuildFlashlight( handle );
}
}


Vector vecOrigin = pRenderable->GetRenderOrigin();
void CClientShadowMgr::UpdateBrushShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle )
Vector start, end, end2;
{
if( !( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) )
{
// Compute the bounding box in the space of the shadow...
Vector mins, maxs;
ComputeHierarchicalBounds( pRenderable, mins, maxs );


VectorMA( vecOrigin, mins.x, vec[0], start );
ShadowType_t shadowType = GetActualShadowCastType( handle );
VectorMA( start, mins.y, vec[1], start );
if ( shadowType != SHADOWS_RENDER_TO_TEXTURE )
VectorMA( start, mins.z, vec[2], start );
{
 
BuildOrthoShadow( pRenderable, handle, mins, maxs );
VectorMA( start, vecSize.x, vec[0], end );
}
VectorMA( end, vecSize.z, vec[2], end2 );
else
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );
{
debugoverlay->AddLineOverlay( end2, end, 255, 0, 0, true, 0.01 );  
BuildRenderToTextureShadow( pRenderable, handle, mins, maxs );
}
}
else
{
VPROF_BUDGET( "CClientShadowMgr::UpdateBrushShadow", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );


VectorMA( start, vecSize.y, vec[1], end );
BuildFlashlight( handle );
VectorMA( end, vecSize.z, vec[2], end2 );
}
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );
}
debugoverlay->AddLineOverlay( end2, end, 255, 0, 0, true, 0.01 );


VectorMA( start, vecSize.z, vec[2], end );
#ifdef _DEBUG
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );
start = end;
VectorMA( start, vecSize.x, vec[0], end );
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );


VectorMA( start, vecSize.y, vec[1], end );
static bool s_bBreak = false;
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  


VectorMA( end, vecSize.x, vec[0], start );
void ShadowBreak_f()
VectorMA( start, -vecSize.x, vec[0], end );
{
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  
s_bBreak = true;
}


VectorMA( start, -vecSize.y, vec[1], end );
static ConCommand r_shadowbreak("r_shadowbreak", ShadowBreak_f);
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  


VectorMA( start, -vecSize.z, vec[2], end );
#endif // _DEBUG
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );


start = end;
bool CClientShadowMgr::WillParentRenderBlobbyShadow( IClientRenderable *pRenderable )
VectorMA( start, -vecSize.x, vec[0], end );
{
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  
if ( !pRenderable )
return false;


VectorMA( start, -vecSize.y, vec[1], end );
IClientRenderable *pShadowParent = pRenderable->GetShadowParent();
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  
if ( !pShadowParent )
return false;
 
// If there's *no* shadow casting type, then we want to see if we can render into its parent
ShadowType_t shadowType = GetActualShadowCastType( pShadowParent );
if ( shadowType == SHADOWS_NONE )
return WillParentRenderBlobbyShadow( pShadowParent );


C_BaseEntity *pEnt = pRenderable->GetIClientUnknown()->GetBaseEntity();
return shadowType == SHADOWS_SIMPLE;
if ( pEnt )
{
debugoverlay->AddTextOverlay( vecOrigin, 0, "%d", pEnt->entindex() );
}
else
{
debugoverlay->AddTextOverlay( vecOrigin, 0, "%X", (size_t)pRenderable );
}
}
}
extern ConVar cl_drawshadowtexture;
extern ConVar cl_shadowtextureoverlaysize;


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Builds a more complex shadow...
// Are we the child of a shadow with render-to-texture?
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::BuildRenderToTextureShadow( IClientRenderable* pRenderable,
bool CClientShadowMgr::ShouldUseParentShadow( IClientRenderable *pRenderable )
ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs)
{
{
if ( cl_drawshadowtexture.GetInt() )
if ( !pRenderable )
{
return false;
// Red wireframe bounding box around objects whose RTT shadows are being updated that frame
DrawRenderToTextureDebugInfo( pRenderable, mins, maxs );
}


// Get the object's basis
IClientRenderable *pShadowParent = pRenderable->GetShadowParent();
Vector vec[3];
if ( !pShadowParent )
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
return false;
vec[1] *= -1.0f;


Vector vecShadowDir = GetShadowDirection( handle );
// Can't render into the parent if the parent is blobby
ShadowType_t shadowType = GetActualShadowCastType( pShadowParent );
if ( shadowType == SHADOWS_SIMPLE )
return false;
 
// If there's *no* shadow casting type, then we want to see if we can render into its parent
if ( shadowType == SHADOWS_NONE )
return ShouldUseParentShadow( pShadowParent );
 
// Here, the parent uses a render-to-texture shadow
return true;
}


// Debugging aid
//-----------------------------------------------------------------------------
// const model_t *pModel = pRenderable->GetModel();
// Before we render any view, make sure all shadows are re-projected vs world
// const char *pDebugName = modelinfo->GetModelName( pModel );
//-----------------------------------------------------------------------------
void CClientShadowMgr::PreRender()
{
VPROF_BUDGET( "CClientShadowMgr::PreRender", VPROF_BUDGETGROUP_SHADOW_RENDERING );
MDLCACHE_CRITICAL_SECTION();


// Project the shadow casting direction into the space of the object
//
Vector localShadowDir;
// -- Shadow Depth Textures -----------------------
localShadowDir[0] = DotProduct( vec[0], vecShadowDir );
//
localShadowDir[1] = DotProduct( vec[1], vecShadowDir );
localShadowDir[2] = DotProduct( vec[2], vecShadowDir );


// Compute the box size
Vector boxSize;
VectorSubtract( maxs, mins, boxSize );
Vector yvec;
float fProjMax = 0.0f;
for( int i = 0; i != 3; ++i )
{
{
Vector test = vec[i] - ( vecShadowDir * DotProduct( vecShadowDir, vec[i] ) );
// VPROF scope
test *= boxSize[i]; //doing after the projection to simplify projection math
VPROF_BUDGET( "CClientShadowMgr::PreRender", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
float fLengthSqr = test.LengthSqr();
 
if( fLengthSqr > fProjMax )
// If someone turned shadow depth mapping on but we can't do it, force it off
{
if ( r_flashlightdepthtexture.GetBool() && !materials->SupportsShadowDepthTextures() )
fProjMax = fLengthSqr;
{
yvec = test;
r_flashlightdepthtexture.SetValue( 0 );
ShutdownDepthTextureShadows();
}
}
}


VectorNormalize( yvec );
bool bDepthTextureActive    = r_flashlightdepthtexture.GetBool();
int  nDepthTextureResolution = r_flashlightdepthres.GetInt();


// Compute the x vector
// If shadow depth texture size or enable/disable changed, do appropriate deallocation/(re)allocation
Vector xvec;
if ( ( bDepthTextureActive != m_bDepthTextureActive ) || ( nDepthTextureResolution != m_nDepthTextureResolution ) )
CrossProduct( yvec, vecShadowDir, xvec );
{
// If shadow depth texturing remains on, but resolution changed, shut down and reinitialize depth textures
if ( ( bDepthTextureActive == true ) && ( m_bDepthTextureActive == true ) &&
( nDepthTextureResolution != m_nDepthTextureResolution ) )
{
ShutdownDepthTextureShadows();
InitDepthTextureShadows();
}
else
{
if ( m_bDepthTextureActive && !bDepthTextureActive ) // Turning off shadow depth texturing
{
ShutdownDepthTextureShadows();
}
else if ( bDepthTextureActive && !m_bDepthTextureActive) // Turning on shadow depth mapping
{
InitDepthTextureShadows();
}
}
}
}


// We project the two longest sides into the vectors perpendicular
//
// to the projection direction, then add in the projection of the perp direction
// -- Render to Texture Shadows -----------------------
Vector2D size;
//
size.x = boxSize.x * fabs( DotProduct( vec[0], xvec ) ) +
boxSize.y * fabs( DotProduct( vec[1], xvec ) ) +
boxSize.z * fabs( DotProduct( vec[2], xvec ) );
size.y = boxSize.x * fabs( DotProduct( vec[0], yvec ) ) +
boxSize.y * fabs( DotProduct( vec[1], yvec ) ) +
boxSize.z * fabs( DotProduct( vec[2], yvec ) );


size.x += 2.0f * TEXEL_SIZE_PER_CASTER_SIZE;
bool bRenderToTextureActive = r_shadowrendertotexture.GetBool();
size.y += 2.0f * TEXEL_SIZE_PER_CASTER_SIZE;
if ( bRenderToTextureActive != m_RenderToTextureActive )
{
if ( m_RenderToTextureActive )
{
ShutdownRenderToTextureShadows();
}
else
{
InitRenderToTextureShadows();
}


// Place the origin at the point with min dot product with shadow dir
UpdateAllShadows();
Vector org;
return;
float falloffStart = ComputeLocalShadowOrigin( pRenderable, mins, maxs, localShadowDir, 1.0f, org );
}


// Transform the local origin into world coordinates
m_bUpdatingDirtyShadows = true;
Vector worldOrigin = pRenderable->GetRenderOrigin( );
VectorMA( worldOrigin, org.x, vec[0], worldOrigin );
VectorMA( worldOrigin, org.y, vec[1], worldOrigin );
VectorMA( worldOrigin, org.z, vec[2], worldOrigin );


VMatrix matWorldToTexture;
unsigned short i = m_DirtyShadows.FirstInorder();
BuildOrthoWorldToShadowMatrix( m_Shadows[handle].m_WorldToShadow, worldOrigin, vecShadowDir, xvec, yvec );
while ( i != m_DirtyShadows.InvalidIndex() )
BuildWorldToTextureMatrix( m_Shadows[handle].m_WorldToShadow, size, matWorldToTexture );
{
Vector2DCopy( size, m_Shadows[handle].m_WorldSize );
MDLCACHE_CRITICAL_SECTION();
ClientShadowHandle_t& handle = m_DirtyShadows[ i ];
UpdateDirtyShadow( handle );
i = m_DirtyShadows.NextInorder(i);
}
m_DirtyShadows.RemoveAll();


// Compute the falloff attenuation
// Transparent shadows must remain dirty, since they were not re-projected
// Area computation isn't exact since xvec is not perp to yvec, but close enough
int nCount = m_TransparentShadows.Count();
// Extra factor of 4 in the maxHeight due to the size being half as big
for ( int i = 0; i < nCount; ++i )
// float shadowArea = size.x * size.y;
{
m_DirtyShadows.Insert( m_TransparentShadows[i] );
}
m_TransparentShadows.RemoveAll();


// The entity may be overriding our shadow cast distance
m_bUpdatingDirtyShadows = false;
float flShadowCastDistance = GetShadowDistance( pRenderable );
float maxHeight = flShadowCastDistance + falloffStart; //3.0f * sqrt( shadowArea );
 
CShadowLeafEnum leafList;
BuildShadowLeafList( &leafList, worldOrigin, vecShadowDir, size, maxHeight );
int nCount = leafList.m_LeafList.Count();
const int *pLeafList = leafList.m_LeafList.Base();
 
shadowmgr->ProjectShadow( m_Shadows[handle].m_ShadowHandle, worldOrigin,
vecShadowDir, matWorldToTexture, size, nCount, pLeafList, maxHeight, falloffStart, MAX_FALLOFF_AMOUNT, pRenderable->GetRenderOrigin() );
 
// Compute extra clip planes to prevent poke-thru
ComputeExtraClipPlanes( pRenderable, handle, vec, mins, maxs, localShadowDir );
 
// Add the shadow to the client leaf system so it correctly marks
// leafs as being affected by a particular shadow
ClientLeafSystem()->ProjectShadow( m_Shadows[handle].m_ClientLeafShadowHandle, nCount, pLeafList );
}
}


static void LineDrawHelper( const Vector &startShadowSpace, const Vector &endShadowSpace,
//-----------------------------------------------------------------------------
  const VMatrix &shadowToWorld, unsigned char r = 255, unsigned char g = 255,
// Gets the entity whose shadow this shadow will render into
  unsigned char b = 255 )
//-----------------------------------------------------------------------------
IClientRenderable *CClientShadowMgr::GetParentShadowEntity( ClientShadowHandle_t handle )
{
{
Vector startWorldSpace, endWorldSpace;
ClientShadow_t& shadow = m_Shadows[handle];
Vector3DMultiplyPositionProjective( shadowToWorld, startShadowSpace, startWorldSpace );
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
Vector3DMultiplyPositionProjective( shadowToWorld, endShadowSpace, endWorldSpace );
if ( pRenderable )
 
{
debugoverlay->AddLineOverlay( startWorldSpace + Vector( 0.0f, 0.0f, 1.0f ),
if ( ShouldUseParentShadow( pRenderable ) )
endWorldSpace + Vector( 0.0f, 0.0f, 1.0f ), r, g, b, false, -1 );
{
IClientRenderable *pParent = pRenderable->GetShadowParent();
while ( GetActualShadowCastType( pParent ) == SHADOWS_NONE )
{
pParent = pParent->GetShadowParent();
Assert( pParent );
}
return pParent;
}
}
return NULL;
}
}


static void DebugDrawFrustum( const Vector &vOrigin, const VMatrix &matWorldToFlashlight )
//-----------------------------------------------------------------------------
// Marks a shadow as needing re-projection
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddToDirtyShadowList( ClientShadowHandle_t handle, bool bForce )
{
{
VMatrix flashlightToWorld;
// Don't add to the dirty shadow list while we're iterating over it
MatrixInverseGeneral( matWorldToFlashlight, flashlightToWorld );
// The only way this can happen is if a child is being rendered into a parent
// shadow, and we don't need it to be added to the dirty list in that case.
// Draw boundaries of frustum
if ( m_bUpdatingDirtyShadows )
LineDrawHelper( Vector( 0.0f, 0.0f, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
return;
LineDrawHelper( Vector( 0.0f, 0.0f, 1.0f ), Vector( 0.0f, 1.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 0.0f, 1.0f, 1.0f ), Vector( 0.0f, 1.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 0.0f, 1.0f, 0.0f ), Vector( 0.0f, 0.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 1.0f, 0.0f, 0.0f ), Vector( 1.0f, 0.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 1.0f, 0.0f, 1.0f ), Vector( 1.0f, 1.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 1.0f, 1.0f, 1.0f ), Vector( 1.0f, 1.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 1.0f, 1.0f, 0.0f ), Vector( 1.0f, 0.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 0.0f, 0.0f, 0.0f ), Vector( 1.0f, 0.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 0.0f, 0.0f, 1.0f ), Vector( 1.0f, 0.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 0.0f, 1.0f, 1.0f ), Vector( 1.0f, 1.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
LineDrawHelper( Vector( 0.0f, 1.0f, 0.0f ), Vector( 1.0f, 1.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );


// Draw RGB triad at front plane
if ( handle == CLIENTSHADOW_INVALID_HANDLE )
LineDrawHelper( Vector( 0.5f, 0.5f, 0.0f ), Vector( 1.0f, 0.5f, 0.0f ),  flashlightToWorld, 255,  0,  0 );
return;
LineDrawHelper( Vector( 0.5f, 0.5f, 0.0f ), Vector( 0.5f, 1.0f, 0.0f ),  flashlightToWorld,  0, 255,  0 );
 
LineDrawHelper( Vector( 0.5f, 0.5f, 0.0f ), Vector( 0.5f, 0.5f, 0.35f ), flashlightToWorld,   0,  0, 255 );
Assert( m_DirtyShadows.Find( handle ) == m_DirtyShadows.InvalidIndex() );
m_DirtyShadows.Insert( handle );
 
// This pretty much guarantees we'll recompute the shadow
if ( bForce )
{
m_Shadows[handle].m_LastAngles.Init( FLT_MAX, FLT_MAX, FLT_MAX );
}
 
// If we use our parent shadow, then it's dirty too...
IClientRenderable *pParent = GetParentShadowEntity( handle );
if ( pParent )
{
AddToDirtyShadowList( pParent, bForce );
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Builds a list of leaves inside the flashlight volume
// Marks a shadow as needing re-projection
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void BuildFlashlightLeafList( CShadowLeafEnum *pEnum, const VMatrix &worldToShadow )
void CClientShadowMgr::AddToDirtyShadowList( IClientRenderable *pRenderable, bool bForce )
{
{
// Use an AABB around the frustum to enumerate leaves.
// Don't add to the dirty shadow list while we're iterating over it
Vector mins, maxs;
// The only way this can happen is if a child is being rendered into a parent
CalculateAABBFromProjectionMatrix( worldToShadow, &mins, &maxs );
// shadow, and we don't need it to be added to the dirty list in that case.
ISpatialQuery* pQuery = engine->GetBSPTreeQuery();
if ( m_bUpdatingDirtyShadows )
pQuery->EnumerateLeavesInBox( mins, maxs, pEnum, 0 );
return;
}


// Are we already in the dirty list?
if ( pRenderable->IsShadowDirty( ) )
return;


void CClientShadowMgr::BuildFlashlight( ClientShadowHandle_t handle )
ClientShadowHandle_t handle = pRenderable->GetShadowHandle();
{
if ( handle == CLIENTSHADOW_INVALID_HANDLE )
// For the 360, we just draw flashlights with the main geometry
return;
// and bypass the entire shadow casting system.
 
ClientShadow_t &shadow = m_Shadows[handle];
#ifdef _DEBUG
if ( IsX360() || r_flashlight_version2.GetInt() )
// Make sure everything's consistent
if ( handle != CLIENTSHADOW_INVALID_HANDLE )
{
{
// This will update the matrices, but not do work to add the flashlight to surfaces
IClientRenderable *pShadowRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[handle].m_Entity );
shadowmgr->ProjectFlashlight( shadow.m_ShadowHandle, shadow.m_WorldToShadow, 0, NULL );
Assert( pRenderable == pShadowRenderable );
return;
}
}
#endif


VPROF_BUDGET( "CClientShadowMgr::BuildFlashlight", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
pRenderable->MarkShadowDirty( true );
AddToDirtyShadowList( handle, bForce );
}


bool bLightModels = r_flashlightmodels.GetBool();
//-----------------------------------------------------------------------------
bool bLightSpecificEntity = shadow.m_hTargetEntity.Get() != NULL;
// Marks the render-to-texture shadow as needing to be re-rendered
bool bLightWorld = ( shadow.m_Flags & SHADOW_FLAGS_LIGHT_WORLD ) != 0;
//-----------------------------------------------------------------------------
int nCount = 0;
void CClientShadowMgr::MarkRenderToTextureShadowDirty( ClientShadowHandle_t handle )
const int *pLeafList = 0;
{
 
// Don't add bogus handles!
CShadowLeafEnum leafList;
if (handle != CLIENTSHADOW_INVALID_HANDLE)
if ( bLightWorld || ( bLightModels && !bLightSpecificEntity ) )
{
{
BuildFlashlightLeafList( &leafList, shadow.m_WorldToShadow );
// Mark the shadow has having a dirty renter-to-texture
nCount = leafList.m_LeafList.Count();
ClientShadow_t& shadow = m_Shadows[handle];
pLeafList = leafList.m_LeafList.Base();
shadow.m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;
}


if( bLightWorld )
// If we use our parent shadow, then it's dirty too...
{
IClientRenderable *pParent = GetParentShadowEntity( handle );
shadowmgr->ProjectFlashlight( shadow.m_ShadowHandle, shadow.m_WorldToShadow, nCount, pLeafList );
if ( pParent )
{
ClientShadowHandle_t parentHandle = pParent->GetShadowHandle();
if ( parentHandle != CLIENTSHADOW_INVALID_HANDLE )
{
m_Shadows[parentHandle].m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;
}
}
}
}
else
}
 
//-----------------------------------------------------------------------------
// Update a shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateShadow( ClientShadowHandle_t handle, bool force )
{
ClientShadow_t& shadow = m_Shadows[handle];
 
// Get the client entity....
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
if ( !pRenderable )
{
{
// This should clear all models and surfaces from this shadow
// Retire the shadow if the entity is gone
shadowmgr->EnableShadow( shadow.m_ShadowHandle, false );
DestroyShadow( handle );
shadowmgr->EnableShadow( shadow.m_ShadowHandle, true );
return;
}
}


if ( !bLightModels )
// Don't bother if there's no model on the renderable
return;
if ( !pRenderable->GetModel() )
 
if ( !bLightSpecificEntity )
{
{
// Add the shadow to the client leaf system so it correctly marks
pRenderable->MarkShadowDirty( false );
// leafs as being affected by a particular shadow
ClientLeafSystem()->ProjectFlashlight( shadow.m_ClientLeafShadowHandle, nCount, pLeafList );
return;
return;
}
}


// We know what we are focused on, so just add the shadow directly to that receiver
// FIXME: NOTE! Because this is called from PreRender, the falloff bias is
Assert( shadow.m_hTargetEntity->GetModel() );
// off by a frame. We could move the code in PreRender to occur after world
 
// list building is done to fix this issue.
C_BaseEntity *pChild = shadow.m_hTargetEntity->FirstMoveChild();
// Don't bother with it if the shadow is totally transparent
while( pChild )
const ShadowInfo_t &shadowInfo = shadowmgr->GetInfo( shadow.m_ShadowHandle );
if ( shadowInfo.m_FalloffBias == 255 )
{
{
int modelType = modelinfo->GetModelType( pChild->GetModel() );
shadowmgr->EnableShadow( shadow.m_ShadowHandle, false );
if (modelType == mod_brush)
m_TransparentShadows.AddToTail( handle );
{
return;
AddShadowToReceiver( handle, pChild, SHADOW_RECEIVER_BRUSH_MODEL );
}
else if ( modelType == mod_studio )
{
AddShadowToReceiver( handle, pChild, SHADOW_RECEIVER_STUDIO_MODEL );
}
 
pChild = pChild->NextMovePeer();
}
}


int modelType = modelinfo->GetModelType( shadow.m_hTargetEntity->GetModel() );
#ifdef _DEBUG
if (modelType == mod_brush)
if (s_bBreak)
{
{
AddShadowToReceiver( handle, shadow.m_hTargetEntity, SHADOW_RECEIVER_BRUSH_MODEL );
s_bBreak = false;
}
}
else if ( modelType == mod_studio )
#endif
// Hierarchical children shouldn't be projecting shadows...
// Check to see if it's a child of an entity with a render-to-texture shadow...
if ( ShouldUseParentShadow( pRenderable ) || WillParentRenderBlobbyShadow( pRenderable ) )
{
{
AddShadowToReceiver( handle, shadow.m_hTargetEntity, SHADOW_RECEIVER_STUDIO_MODEL );
shadowmgr->EnableShadow( shadow.m_ShadowHandle, false );
pRenderable->MarkShadowDirty( false );
return;
}
}
}


shadowmgr->EnableShadow( shadow.m_ShadowHandle, true );


//-----------------------------------------------------------------------------
// Figure out if the shadow moved...
// Adds the child bounds to the bounding box
// Even though we have dirty bits, some entities
//-----------------------------------------------------------------------------
// never clear those dirty bits
void CClientShadowMgr::AddChildBounds( matrix3x4_t &matWorldToBBox, IClientRenderable* pParent, Vector &vecMins, Vector &vecMaxs )
const Vector& origin = pRenderable->GetRenderOrigin();
{
const QAngle& angles = pRenderable->GetRenderAngles();
Vector vecChildMins, vecChildMaxs;
Vector vecNewChildMins, vecNewChildMaxs;
matrix3x4_t childToBBox;


IClientRenderable *pChild = pParent->FirstShadowChild();
if ( force || (origin != shadow.m_LastOrigin) || (angles != shadow.m_LastAngles) || shadow.m_LightPosLerp < 1.0f )
while( pChild )
{
{
// Transform the child bbox into the space of the main bbox
// Store off the new pos/orientation
// FIXME: Optimize this?
VectorCopy( origin, shadow.m_LastOrigin );
if ( GetActualShadowCastType( pChild ) != SHADOWS_NONE)
VectorCopy( angles, shadow.m_LastAngles );
 
CMatRenderContextPtr pRenderContext( materials );
const model_t *pModel = pRenderable->GetModel();
MaterialFogMode_t fogMode = pRenderContext->GetFogMode();
pRenderContext->FogMode( MATERIAL_FOG_NONE );
switch( modelinfo->GetModelType( pModel ) )
{
{
pChild->GetShadowRenderBounds( vecChildMins, vecChildMaxs, SHADOWS_RENDER_TO_TEXTURE );
case mod_brush:
ConcatTransforms( matWorldToBBox, pChild->RenderableToWorldTransform(), childToBBox );
UpdateBrushShadow( pRenderable, handle );
TransformAABB( childToBBox, vecChildMins, vecChildMaxs, vecNewChildMins, vecNewChildMaxs );
break;
VectorMin( vecMins, vecNewChildMins, vecMins );
 
VectorMax( vecMaxs, vecNewChildMaxs, vecMaxs );
case mod_studio:
UpdateStudioShadow( pRenderable, handle );
break;
 
default:
// Shouldn't get here if not a brush or studio
Assert(0);
break;
}
}
 
pRenderContext->FogMode( fogMode );
AddChildBounds( matWorldToBBox, pChild, vecMins, vecMaxs );
pChild = pChild->NextShadowPeer();
}
}
}
 
 
// NOTE: We can't do this earlier because pEnt->GetRenderOrigin() can
// provoke a recomputation of render origin, which, for aiments, can cause everything
// to be marked as dirty. So don't clear the flag until this point.
pRenderable->MarkShadowDirty( false );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Compute a bounds for the entity + children
// Update a shadow
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeHierarchicalBounds( IClientRenderable *pRenderable, Vector &vecMins, Vector &vecMaxs )
void CClientShadowMgr::UpdateProjectedTextureInternal( ClientShadowHandle_t handle, bool force )
{
{
ShadowType_t shadowType = GetActualShadowCastType( pRenderable );
ClientShadow_t& shadow = m_Shadows[handle];
 
if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
{
VPROF_BUDGET( "CClientShadowMgr::UpdateProjectedTextureInternal", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
 
Assert( ( shadow.m_Flags & SHADOW_FLAGS_SHADOW ) == 0 );
ClientShadow_t& shadow = m_Shadows[handle];


pRenderable->GetShadowRenderBounds( vecMins, vecMaxs, shadowType );
shadowmgr->EnableShadow( shadow.m_ShadowHandle, true );


// We could use a good solution for this in the regular PC build, since
// FIXME: What's the difference between brush and model shadows for light projectors? Answer: nothing.
// it causes lots of extra bone setups for entities you can't see.
UpdateBrushShadow( NULL, handle );
if ( IsPC() )
}
else
{
{
IClientRenderable *pChild = pRenderable->FirstShadowChild();
Assert( shadow.m_Flags & SHADOW_FLAGS_SHADOW );
 
Assert( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 );
// Don't recurse down the tree when we hit a blobby shadow
UpdateShadow( handle, force );
if ( pChild && shadowType != SHADOWS_SIMPLE )
{
matrix3x4_t matWorldToBBox;
MatrixInvert( pRenderable->RenderableToWorldTransform(), matWorldToBBox );
AddChildBounds( matWorldToBBox, pRenderable, vecMins, vecMaxs );
}
}
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Shadow update functions
// Update a shadow
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateStudioShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle )
void CClientShadowMgr::UpdateProjectedTexture( ClientShadowHandle_t handle, bool force )
{
{
if( !( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) )
VPROF_BUDGET( "CClientShadowMgr::UpdateProjectedTexture", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
{
 
Vector mins, maxs;
if ( handle == CLIENTSHADOW_INVALID_HANDLE )
ComputeHierarchicalBounds( pRenderable, mins, maxs );
return;


ShadowType_t shadowType = GetActualShadowCastType( handle );
// NOTE: This can only work for flashlights, since UpdateProjectedTextureInternal
if ( shadowType != SHADOWS_RENDER_TO_TEXTURE )
// depends on the falloff offset to cull shadows.
{
ClientShadow_t &shadow = m_Shadows[ handle ];
BuildOrthoShadow( pRenderable, handle, mins, maxs );
if( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 )
}
else
{
BuildRenderToTextureShadow( pRenderable, handle, mins, maxs );
}
}
else
{
{
BuildFlashlight( handle );
Warning( "CClientShadowMgr::UpdateProjectedTexture can only be used with flashlights!\n" );
return;
}
}
UpdateProjectedTextureInternal( handle, force );
RemoveShadowFromDirtyList( handle );
}
}


void CClientShadowMgr::UpdateBrushShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle )
//-----------------------------------------------------------------------------
// Computes bounding sphere
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeBoundingSphere( IClientRenderable* pRenderable, Vector& origin, float& radius )
{
{
if( !( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) )
Assert( pRenderable );
{
Vector mins, maxs;
// Compute the bounding box in the space of the shadow...
pRenderable->GetShadowRenderBounds( mins, maxs, GetActualShadowCastType( pRenderable ) );
Vector mins, maxs;
Vector size;
ComputeHierarchicalBounds( pRenderable, mins, maxs );
VectorSubtract( maxs, mins, size );
radius = size.Length() * 0.5f;
 
// Compute centroid (local space)
Vector centroid;
VectorAdd( mins, maxs, centroid );
centroid *= 0.5f;
 
// Transform centroid into world space
Vector vec[3];
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
vec[1] *= -1.0f;
 
VectorCopy( pRenderable->GetRenderOrigin(), origin );
VectorMA( origin, centroid.x, vec[0], origin );
VectorMA( origin, centroid.y, vec[1], origin );
VectorMA( origin, centroid.z, vec[2], origin );
}
 
//-----------------------------------------------------------------------------
// Computes a rough AABB encompassing the volume of the shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeShadowBBox( IClientRenderable *pRenderable, ClientShadowHandle_t shadowHandle, const Vector &vecAbsCenter, float flRadius, Vector *pAbsMins, Vector *pAbsMaxs )
{
// This is *really* rough. Basically we simply determine the
// maximum shadow casting length and extrude the box by that distance


ShadowType_t shadowType = GetActualShadowCastType( handle );
Vector vecShadowDir = GetShadowDirection( shadowHandle );
if ( shadowType != SHADOWS_RENDER_TO_TEXTURE )
for (int i = 0; i < 3; ++i)
{
float flShadowCastDistance = GetShadowDistance( pRenderable );
float flDist = flShadowCastDistance * vecShadowDir[i];
 
if (vecShadowDir[i] < 0)
{
{
BuildOrthoShadow( pRenderable, handle, mins, maxs );
(*pAbsMins)[i] = vecAbsCenter[i] - flRadius + flDist;
(*pAbsMaxs)[i] = vecAbsCenter[i] + flRadius;
}
}
else
else
{
{
BuildRenderToTextureShadow( pRenderable, handle, mins, maxs );
(*pAbsMins)[i] = vecAbsCenter[i] - flRadius;
(*pAbsMaxs)[i] = vecAbsCenter[i] + flRadius + flDist;
}
}
}
}
else
}
{
VPROF_BUDGET( "CClientShadowMgr::UpdateBrushShadow", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );


BuildFlashlight( handle );
//-----------------------------------------------------------------------------
}
// Compute a separating axis...
//-----------------------------------------------------------------------------
bool CClientShadowMgr::ComputeSeparatingPlane( IClientRenderable* pRend1, IClientRenderable* pRend2, cplane_t* pPlane )
{
Vector min1, max1, min2, max2;
pRend1->GetShadowRenderBounds( min1, max1, GetActualShadowCastType( pRend1 ) );
pRend2->GetShadowRenderBounds( min2, max2, GetActualShadowCastType( pRend2 ) );
return ::ComputeSeparatingPlane(
pRend1->GetRenderOrigin(), pRend1->GetRenderAngles(), min1, max1,
pRend2->GetRenderOrigin(), pRend2->GetRenderAngles(), min2, max2,
3.0f, pPlane );
}
}


 
//-----------------------------------------------------------------------------
#ifdef _DEBUG
// Cull shadows based on rough bounding volumes
 
//-----------------------------------------------------------------------------
static bool s_bBreak = false;
bool CClientShadowMgr::CullReceiver( ClientShadowHandle_t handle, IClientRenderable* pRenderable,
 
IClientRenderable* pSourceRenderable )
void ShadowBreak_f()
{
{
s_bBreak = true;
// check flags here instead and assert !pSourceRenderable
}
if( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT )
{
VPROF_BUDGET( "CClientShadowMgr::CullReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );


static ConCommand r_shadowbreak("r_shadowbreak", ShadowBreak_f);
Assert( !pSourceRenderable );
const Frustum_t &frustum = shadowmgr->GetFlashlightFrustum( m_Shadows[handle].m_ShadowHandle );


#endif // _DEBUG
Vector mins, maxs;
pRenderable->GetRenderBoundsWorldspace( mins, maxs );


return R_CullBox( mins, maxs, frustum );
}


bool CClientShadowMgr::WillParentRenderBlobbyShadow( IClientRenderable *pRenderable )
Assert( pSourceRenderable );
{
// Compute a bounding sphere for the renderable
if ( !pRenderable )
Vector origin;
return false;
float radius;
ComputeBoundingSphere( pRenderable, origin, radius );


IClientRenderable *pShadowParent = pRenderable->GetShadowParent();
// Transform the sphere center into the space of the shadow
if ( !pShadowParent )
Vector localOrigin;
return false;
const ClientShadow_t& shadow = m_Shadows[handle];
const ShadowInfo_t& info = shadowmgr->GetInfo( shadow.m_ShadowHandle );
Vector3DMultiplyPosition( shadow.m_WorldToShadow, origin, localOrigin );


// If there's *no* shadow casting type, then we want to see if we can render into its parent
// Compute a rough bounding box for the shadow (in shadow space)
ShadowType_t shadowType = GetActualShadowCastType( pShadowParent );
Vector shadowMin, shadowMax;
if ( shadowType == SHADOWS_NONE )
shadowMin.Init( -shadow.m_WorldSize.x * 0.5f, -shadow.m_WorldSize.y * 0.5f, 0 );
return WillParentRenderBlobbyShadow( pShadowParent );
shadowMax.Init( shadow.m_WorldSize.x * 0.5f, shadow.m_WorldSize.y * 0.5f, info.m_MaxDist );


return shadowType == SHADOWS_SIMPLE;
// If the bounding sphere doesn't intersect with the shadow volume, cull
}
if (!IsBoxIntersectingSphere( shadowMin, shadowMax, localOrigin, radius ))
return true;


Vector originSource;
float radiusSource;
ComputeBoundingSphere( pSourceRenderable, originSource, radiusSource );


//-----------------------------------------------------------------------------
// Fast check for separating plane...
// Are we the child of a shadow with render-to-texture?
bool foundSeparatingPlane = false;
//-----------------------------------------------------------------------------
cplane_t plane;
bool CClientShadowMgr::ShouldUseParentShadow( IClientRenderable *pRenderable )
if (!IsSphereIntersectingSphere( originSource, radiusSource, origin, radius ))
{
{
if ( !pRenderable )
foundSeparatingPlane = true;
return false;


IClientRenderable *pShadowParent = pRenderable->GetShadowParent();
// the plane normal doesn't need to be normalized...
if ( !pShadowParent )
VectorSubtract( origin, originSource, plane.normal );
return false;
}
 
else
// Can't render into the parent if the parent is blobby
{
ShadowType_t shadowType = GetActualShadowCastType( pShadowParent );
foundSeparatingPlane = ComputeSeparatingPlane( pRenderable, pSourceRenderable, &plane );
if ( shadowType == SHADOWS_SIMPLE )
}
return false;
 
// If there's *no* shadow casting type, then we want to see if we can render into its parent
if ( shadowType == SHADOWS_NONE )
return ShouldUseParentShadow( pShadowParent );
 
// Here, the parent uses a render-to-texture shadow
return true;
}
 
 
//-----------------------------------------------------------------------------
// Before we render any view, make sure all shadows are re-projected vs world
//-----------------------------------------------------------------------------
void CClientShadowMgr::PreRender()
{
VPROF_BUDGET( "CClientShadowMgr::PreRender", VPROF_BUDGETGROUP_SHADOW_RENDERING );
MDLCACHE_CRITICAL_SECTION();
 
//
// -- Shadow Depth Textures -----------------------
//


if (foundSeparatingPlane)
{
{
// VPROF scope
// Compute which side of the plane the renderable is on..
VPROF_BUDGET( "CClientShadowMgr::PreRender", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
    Vector vecShadowDir = GetShadowDirection( handle );
float shadowDot = DotProduct( vecShadowDir, plane.normal );
float receiverDot = DotProduct( plane.normal, origin );
float sourceDot = DotProduct( plane.normal, originSource );


// If someone turned shadow depth mapping on but we can't do it, force it off
if (shadowDot > 0.0f)
if ( r_flashlightdepthtexture.GetBool() && !materials->SupportsShadowDepthTextures() )
{
{
r_flashlightdepthtexture.SetValue( 0 );
if (receiverDot <= sourceDot)
ShutdownDepthTextureShadows();
{
// Vector dest;
// VectorMA( pSourceRenderable->GetRenderOrigin(), 50, plane.normal, dest );
// debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 255, 0, true, 1.0f );
return true;
}
else
{
// Vector dest;
// VectorMA( pSourceRenderable->GetRenderOrigin(), 50, plane.normal, dest );
// debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 0, 0, true, 1.0f );
}
}
}
 
else
bool bDepthTextureActive    = r_flashlightdepthtexture.GetBool();
int  nDepthTextureResolution = r_flashlightdepthres.GetInt();
 
// If shadow depth texture size or enable/disable changed, do appropriate deallocation/(re)allocation
if ( ( bDepthTextureActive != m_bDepthTextureActive ) || ( nDepthTextureResolution != m_nDepthTextureResolution ) )
{
{
// If shadow depth texturing remains on, but resolution changed, shut down and reinitialize depth textures
if (receiverDot >= sourceDot)
if ( ( bDepthTextureActive == true ) && ( m_bDepthTextureActive == true ) &&
( nDepthTextureResolution != m_nDepthTextureResolution ) )
{
{
ShutdownDepthTextureShadows();
// Vector dest;
InitDepthTextureShadows();
// VectorMA( pSourceRenderable->GetRenderOrigin(), -50, plane.normal, dest );  
// debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 255, 0, true, 1.0f );
return true;
}
}
else
else
{
{
if ( m_bDepthTextureActive && !bDepthTextureActive ) // Turning off shadow depth texturing
// Vector dest;
{
// VectorMA( pSourceRenderable->GetRenderOrigin(), 50, plane.normal, dest );  
ShutdownDepthTextureShadows();
// debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 0, 0, true, 1.0f );
}
else if ( bDepthTextureActive && !m_bDepthTextureActive) // Turning on shadow depth mapping
{
InitDepthTextureShadows();
}
}
}
}
}
}
}


//
// No additional clip planes? ok then it's a valid receiver
// -- Render to Texture Shadows -----------------------
/*
//
if (shadow.m_ClipPlaneCount == 0)
return false;


bool bRenderToTextureActive = r_shadowrendertotexture.GetBool();
// Check the additional cull planes
if ( bRenderToTextureActive != m_RenderToTextureActive )
int i;
for ( i = 0; i < shadow.m_ClipPlaneCount; ++i)
{
{
if ( m_RenderToTextureActive )
// Fast sphere cull
{
if (DotProduct( origin, shadow.m_ClipPlane[i] ) - radius > shadow.m_ClipDist[i])
ShutdownRenderToTextureShadows();
return true;
}
else
{
InitRenderToTextureShadows();
}
 
UpdateAllShadows();
return;
}
}


m_bUpdatingDirtyShadows = true;
// More expensive box on plane side cull...
Vector vec[3];
Vector mins, maxs;
cplane_t plane;
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
pRenderable->GetBounds( mins, maxs );


unsigned short i = m_DirtyShadows.FirstInorder();
for ( i = 0; i < shadow.m_ClipPlaneCount; ++i)
while ( i != m_DirtyShadows.InvalidIndex() )
{
{
MDLCACHE_CRITICAL_SECTION();
// Transform the plane into the space of the receiver
ClientShadowHandle_t& handle = m_DirtyShadows[ i ];
plane.normal.x = DotProduct( vec[0], shadow.m_ClipPlane[i] );
UpdateDirtyShadow( handle );
plane.normal.y = DotProduct( vec[1], shadow.m_ClipPlane[i] );
i = m_DirtyShadows.NextInorder(i);
plane.normal.z = DotProduct( vec[2], shadow.m_ClipPlane[i] );
}
 
m_DirtyShadows.RemoveAll();
plane.dist = shadow.m_ClipDist[i] - DotProduct( shadow.m_ClipPlane[i], pRenderable->GetRenderOrigin() );


// Transparent shadows must remain dirty, since they were not re-projected
// If the box is on the front side of the plane, we're done.
int nCount = m_TransparentShadows.Count();
if (BoxOnPlaneSide2( mins, maxs, &plane, 3.0f ) == 1)
for ( int i = 0; i < nCount; ++i )
return true;
{
m_DirtyShadows.Insert( m_TransparentShadows[i] );
}
}
m_TransparentShadows.RemoveAll();
*/


m_bUpdatingDirtyShadows = false;
return false;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Gets the entity whose shadow this shadow will render into
// deals with shadows being added to shadow receivers
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
IClientRenderable *CClientShadowMgr::GetParentShadowEntity( ClientShadowHandle_t handle )
void CClientShadowMgr::AddShadowToReceiver( ClientShadowHandle_t handle,
IClientRenderable* pRenderable, ShadowReceiver_t type )
{
{
ClientShadow_t& shadow = m_Shadows[handle];
ClientShadow_t &shadow = m_Shadows[handle];
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
if ( pRenderable )
{
if ( ShouldUseParentShadow( pRenderable ) )
{
IClientRenderable *pParent = pRenderable->GetShadowParent();
while ( GetActualShadowCastType( pParent ) == SHADOWS_NONE )
{
pParent = pParent->GetShadowParent();
Assert( pParent );
}
return pParent;
}
}
return NULL;
}


// Don't add a shadow cast by an object to itself...
IClientRenderable* pSourceRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );


//-----------------------------------------------------------------------------
// NOTE: if pSourceRenderable == NULL, the source is probably a flashlight since there is no entity.
// Marks a shadow as needing re-projection
if (pSourceRenderable == pRenderable)
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddToDirtyShadowList( ClientShadowHandle_t handle, bool bForce )
{
// Don't add to the dirty shadow list while we're iterating over it
// The only way this can happen is if a child is being rendered into a parent
// shadow, and we don't need it to be added to the dirty list in that case.
if ( m_bUpdatingDirtyShadows )
return;
return;


if ( handle == CLIENTSHADOW_INVALID_HANDLE )
// Don't bother if this renderable doesn't receive shadows or light from flashlights
if( !pRenderable->ShouldReceiveProjectedTextures( SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ) )
return;
return;


Assert( m_DirtyShadows.Find( handle ) == m_DirtyShadows.InvalidIndex() );
// Cull if the origin is on the wrong side of a shadow clip plane....
m_DirtyShadows.Insert( handle );
if ( CullReceiver( handle, pRenderable, pSourceRenderable ) )
return;


// This pretty much guarantees we'll recompute the shadow
// Do different things depending on the receiver type
if ( bForce )
switch( type )
{
{
m_Shadows[handle].m_LastAngles.Init( FLT_MAX, FLT_MAX, FLT_MAX );
case SHADOW_RECEIVER_BRUSH_MODEL:
}


// If we use our parent shadow, then it's dirty too...
if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
IClientRenderable *pParent = GetParentShadowEntity( handle );
{
if ( pParent )
VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
{
AddToDirtyShadowList( pParent, bForce );
}
}


if( (!shadow.m_hTargetEntity) || IsFlashlightTarget( handle, pRenderable ) )
{
shadowmgr->AddShadowToBrushModel( shadow.m_ShadowHandle,
const_cast<model_t*>(pRenderable->GetModel()),
pRenderable->GetRenderOrigin(), pRenderable->GetRenderAngles() );


//-----------------------------------------------------------------------------
shadowmgr->AddFlashlightRenderable( shadow.m_ShadowHandle, pRenderable );
// Marks a shadow as needing re-projection
}
//-----------------------------------------------------------------------------
}
void CClientShadowMgr::AddToDirtyShadowList( IClientRenderable *pRenderable, bool bForce )
else
{
{
// Don't add to the dirty shadow list while we're iterating over it
shadowmgr->AddShadowToBrushModel( shadow.m_ShadowHandle,
// The only way this can happen is if a child is being rendered into a parent
const_cast<model_t*>(pRenderable->GetModel()),
// shadow, and we don't need it to be added to the dirty list in that case.
pRenderable->GetRenderOrigin(), pRenderable->GetRenderAngles() );
if ( m_bUpdatingDirtyShadows )
}
return;
break;


// Are we already in the dirty list?
case SHADOW_RECEIVER_STATIC_PROP:
if ( pRenderable->IsShadowDirty( ) )
// Don't add shadows to props if we're not using render-to-texture
return;
if ( GetActualShadowCastType( handle ) == SHADOWS_RENDER_TO_TEXTURE )
{
// Also don't add them unless an NPC or player casts them..
// They are wickedly expensive!!!
C_BaseEntity *pEnt = pSourceRenderable->GetIClientUnknown()->GetBaseEntity();
if ( pEnt && ( pEnt->GetFlags() & (FL_NPC | FL_CLIENT)) )
{
staticpropmgr->AddShadowToStaticProp( shadow.m_ShadowHandle, pRenderable );
}
}
else if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
{
VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );


ClientShadowHandle_t handle = pRenderable->GetShadowHandle();
if( (!shadow.m_hTargetEntity) || IsFlashlightTarget( handle, pRenderable ) )
if ( handle == CLIENTSHADOW_INVALID_HANDLE )
{
return;
staticpropmgr->AddShadowToStaticProp( shadow.m_ShadowHandle, pRenderable );


#ifdef _DEBUG
shadowmgr->AddFlashlightRenderable( shadow.m_ShadowHandle, pRenderable );
// Make sure everything's consistent
}
if ( handle != CLIENTSHADOW_INVALID_HANDLE )
}
{
break;
IClientRenderable *pShadowRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[handle].m_Entity );
Assert( pRenderable == pShadowRenderable );
}
#endif


pRenderable->MarkShadowDirty( true );
case SHADOW_RECEIVER_STUDIO_MODEL:
AddToDirtyShadowList( handle, bForce );
if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
}
{
VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );


 
if( (!shadow.m_hTargetEntity) || IsFlashlightTarget( handle, pRenderable ) )
//-----------------------------------------------------------------------------
// Marks the render-to-texture shadow as needing to be re-rendered
//-----------------------------------------------------------------------------
void CClientShadowMgr::MarkRenderToTextureShadowDirty( ClientShadowHandle_t handle )
{
// Don't add bogus handles!
if (handle != CLIENTSHADOW_INVALID_HANDLE)
{
// Mark the shadow has having a dirty renter-to-texture
ClientShadow_t& shadow = m_Shadows[handle];
shadow.m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;
 
// If we use our parent shadow, then it's dirty too...
IClientRenderable *pParent = GetParentShadowEntity( handle );
if ( pParent )
{
ClientShadowHandle_t parentHandle = pParent->GetShadowHandle();
if ( parentHandle != CLIENTSHADOW_INVALID_HANDLE )
{
{
m_Shadows[parentHandle].m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;
pRenderable->CreateModelInstance();
shadowmgr->AddShadowToModel( shadow.m_ShadowHandle, pRenderable->GetModelInstance() );
shadowmgr->AddFlashlightRenderable( shadow.m_ShadowHandle, pRenderable );
}
}
}
}
break;
// default:
}
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Update a shadow
// deals with shadows being added to shadow receivers
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateShadow( ClientShadowHandle_t handle, bool force )
void CClientShadowMgr::RemoveAllShadowsFromReceiver(  
IClientRenderable* pRenderable, ShadowReceiver_t type )
{
{
ClientShadow_t& shadow = m_Shadows[handle];
// Don't bother if this renderable doesn't receive shadows
 
if ( !pRenderable->ShouldReceiveProjectedTextures( SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ) )
// Get the client entity....
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
if ( !pRenderable )
{
// Retire the shadow if the entity is gone
DestroyShadow( handle );
return;
return;
}


// Don't bother if there's no model on the renderable
// Do different things depending on the receiver type
if ( !pRenderable->GetModel() )
switch( type )
{
{
pRenderable->MarkShadowDirty( false );
case SHADOW_RECEIVER_BRUSH_MODEL:
return;
{
}
model_t* pModel = const_cast<model_t*>(pRenderable->GetModel());
shadowmgr->RemoveAllShadowsFromBrushModel( pModel );
}
break;


// FIXME: NOTE! Because this is called from PreRender, the falloff bias is
case SHADOW_RECEIVER_STATIC_PROP:
// off by a frame. We could move the code in PreRender to occur after world
staticpropmgr->RemoveAllShadowsFromStaticProp(pRenderable);
// list building is done to fix this issue.
break;
// Don't bother with it if the shadow is totally transparent
const ShadowInfo_t &shadowInfo = shadowmgr->GetInfo( shadow.m_ShadowHandle );
if ( shadowInfo.m_FalloffBias == 255 )
{
shadowmgr->EnableShadow( shadow.m_ShadowHandle, false );
m_TransparentShadows.AddToTail( handle );
return;
}


#ifdef _DEBUG
case SHADOW_RECEIVER_STUDIO_MODEL:
if (s_bBreak)
if( pRenderable && pRenderable->GetModelInstance() != MODEL_INSTANCE_INVALID )
{
{
s_bBreak = false;
shadowmgr->RemoveAllShadowsFromModel( pRenderable->GetModelInstance() );
}
}
#endif
break;
// Hierarchical children shouldn't be projecting shadows...
 
// Check to see if it's a child of an entity with a render-to-texture shadow...
// default:
if ( ShouldUseParentShadow( pRenderable ) || WillParentRenderBlobbyShadow( pRenderable ) )
// // FIXME: How do deal with this stuff? Add a method to IClientRenderable?
{
// C_BaseEntity* pEnt = static_cast<C_BaseEntity*>(pRenderable);
shadowmgr->EnableShadow( shadow.m_ShadowHandle, false );
// pEnt->RemoveAllShadows();
pRenderable->MarkShadowDirty( false );
return;
}
}
}


shadowmgr->EnableShadow( shadow.m_ShadowHandle, true );
//-----------------------------------------------------------------------------
// Computes + sets the render-to-texture texcoords
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetRenderToTextureShadowTexCoords( ShadowHandle_t handle, int x, int y, int w, int h )
{
// Let the shadow mgr know about the texture coordinates...
// That way it'll be able to batch rendering better.
int textureW, textureH;
m_ShadowAllocator.GetTotalTextureSize( textureW, textureH );


// Figure out if the shadow moved...
// Go in a half-pixel to avoid blending with neighboring textures..
// Even though we have dirty bits, some entities
float u, v, du, dv;
// never clear those dirty bits
const Vector& origin = pRenderable->GetRenderOrigin();
const QAngle& angles = pRenderable->GetRenderAngles();


if ( force || (origin != shadow.m_LastOrigin) || (angles != shadow.m_LastAngles) || shadow.m_LightPosLerp < 1.0f )
u  = ((float)x + 0.5f) / (float)textureW;
{
= ((float)y + 0.5f) / (float)textureH;
// Store off the new pos/orientation
du = ((float)w - 1) / (float)textureW;
VectorCopy( origin, shadow.m_LastOrigin );
dv = ((float)h - 1) / (float)textureH;
VectorCopy( angles, shadow.m_LastAngles );


CMatRenderContextPtr pRenderContext( materials );
shadowmgr->SetShadowTexCoord( handle, u, v, du, dv );
const model_t *pModel = pRenderable->GetModel();
MaterialFogMode_t fogMode = pRenderContext->GetFogMode();
pRenderContext->FogMode( MATERIAL_FOG_NONE );
switch( modelinfo->GetModelType( pModel ) )
{
case mod_brush:
UpdateBrushShadow( pRenderable, handle );
break;
 
case mod_studio:
UpdateStudioShadow( pRenderable, handle );
break;
 
default:
// Shouldn't get here if not a brush or studio
Assert(0);
break;
}
pRenderContext->FogMode( fogMode );
}
 
// NOTE: We can't do this earlier because pEnt->GetRenderOrigin() can
// provoke a recomputation of render origin, which, for aiments, can cause everything
// to be marked as dirty. So don't clear the flag until this point.
pRenderable->MarkShadowDirty( false );
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Update a shadow
// Setup all children shadows
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateProjectedTextureInternal( ClientShadowHandle_t handle, bool force )
bool CClientShadowMgr::BuildSetupShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild )
{
{
ClientShadow_t& shadow = m_Shadows[handle];
bool bDrewTexture = false;
 
// Stop traversing when we hit a blobby shadow
ShadowType_t shadowType = GetActualShadowCastType( pRenderable );
if ( pRenderable && shadowType == SHADOWS_SIMPLE )
return false;


if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
if ( !pRenderable || shadowType != SHADOWS_NONE )
{
{
VPROF_BUDGET( "CClientShadowMgr::UpdateProjectedTextureInternal", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
bool bDrawModelShadow;
if ( !bChild )
{
bDrawModelShadow = ((shadow.m_Flags & SHADOW_FLAGS_BRUSH_MODEL) == 0);
}
else
{
int nModelType = modelinfo->GetModelType( pRenderable->GetModel() );
bDrawModelShadow = nModelType == mod_studio;
}


Assert( ( shadow.m_Flags & SHADOW_FLAGS_SHADOW ) == 0 );
if ( bDrawModelShadow )
ClientShadow_t& shadow = m_Shadows[handle];
{
C_BaseEntity *pEntity = pRenderable->GetIClientUnknown()->GetBaseEntity();
if ( pEntity )
{
if ( pEntity->IsNPC() )
{
s_NPCShadowBoneSetups.AddToTail( assert_cast<C_BaseAnimating *>( pEntity ) );
}
else if ( pEntity->GetBaseAnimating() )
{
s_NonNPCShadowBoneSetups.AddToTail( assert_cast<C_BaseAnimating *>( pEntity ) );
}


shadowmgr->EnableShadow( shadow.m_ShadowHandle, true );
}
 
bDrewTexture = true;
// FIXME: What's the difference between brush and model shadows for light projectors? Answer: nothing.
}
UpdateBrushShadow( NULL, handle );
}
else
{
Assert( shadow.m_Flags & SHADOW_FLAGS_SHADOW );
Assert( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 );
UpdateShadow( handle, force );
}
}
}


if ( !pRenderable )
return bDrewTexture;


//-----------------------------------------------------------------------------
IClientRenderable *pChild;
// Update a shadow
for ( pChild = pRenderable->FirstShadowChild(); pChild; pChild = pChild->NextShadowPeer() )
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateProjectedTexture( ClientShadowHandle_t handle, bool force )
{
VPROF_BUDGET( "CClientShadowMgr::UpdateProjectedTexture", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
 
if ( handle == CLIENTSHADOW_INVALID_HANDLE )
return;
 
// NOTE: This can only work for flashlights, since UpdateProjectedTextureInternal
// depends on the falloff offset to cull shadows.
ClientShadow_t &shadow = m_Shadows[ handle ];
if( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 )
{
{
Warning( "CClientShadowMgr::UpdateProjectedTexture can only be used with flashlights!\n" );
if ( BuildSetupShadowHierarchy( pChild, shadow, true ) )
return;
{
bDrewTexture = true;
}
}
}
 
return bDrewTexture;
UpdateProjectedTextureInternal( handle, force );
RemoveShadowFromDirtyList( handle );
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Computes bounding sphere
// Draws all children shadows into our own
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeBoundingSphere( IClientRenderable* pRenderable, Vector& origin, float& radius )
bool CClientShadowMgr::DrawShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild )
{
{
Assert( pRenderable );
bool bDrewTexture = false;
Vector mins, maxs;
pRenderable->GetShadowRenderBounds( mins, maxs, GetActualShadowCastType( pRenderable ) );
Vector size;
VectorSubtract( maxs, mins, size );
radius = size.Length() * 0.5f;


// Compute centroid (local space)
// Stop traversing when we hit a blobby shadow
Vector centroid;
ShadowType_t shadowType = GetActualShadowCastType( pRenderable );
VectorAdd( mins, maxs, centroid );
if ( pRenderable && shadowType == SHADOWS_SIMPLE )
centroid *= 0.5f;
return false;


// Transform centroid into world space
if ( !pRenderable || shadowType != SHADOWS_NONE )
Vector vec[3];
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
vec[1] *= -1.0f;
 
VectorCopy( pRenderable->GetRenderOrigin(), origin );
VectorMA( origin, centroid.x, vec[0], origin );
VectorMA( origin, centroid.y, vec[1], origin );
VectorMA( origin, centroid.z, vec[2], origin );
}
 
 
//-----------------------------------------------------------------------------
// Computes a rough AABB encompassing the volume of the shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeShadowBBox( IClientRenderable *pRenderable, ClientShadowHandle_t shadowHandle, const Vector &vecAbsCenter, float flRadius, Vector *pAbsMins, Vector *pAbsMaxs )
{
// This is *really* rough. Basically we simply determine the
// maximum shadow casting length and extrude the box by that distance
 
Vector vecShadowDir = GetShadowDirection( shadowHandle );
for (int i = 0; i < 3; ++i)
{
{
float flShadowCastDistance = GetShadowDistance( pRenderable );
bool bDrawModelShadow;
float flDist = flShadowCastDistance * vecShadowDir[i];
bool bDrawBrushShadow;
 
if ( !bChild )
if (vecShadowDir[i] < 0)
{
{
(*pAbsMins)[i] = vecAbsCenter[i] - flRadius + flDist;
bDrawModelShadow = ((shadow.m_Flags & SHADOW_FLAGS_BRUSH_MODEL) == 0);
(*pAbsMaxs)[i] = vecAbsCenter[i] + flRadius;
bDrawBrushShadow = !bDrawModelShadow;
}
}
else
else
{
{
(*pAbsMins)[i] = vecAbsCenter[i] - flRadius;
int nModelType = modelinfo->GetModelType( pRenderable->GetModel() );
(*pAbsMaxs)[i] = vecAbsCenter[i] + flRadius + flDist;
bDrawModelShadow = nModelType == mod_studio;
bDrawBrushShadow = nModelType == mod_brush;
}
}
}
   
}
if ( bDrawModelShadow )
 
{
DrawModelInfo_t info;
matrix3x4_t *pBoneToWorld = modelrender->DrawModelShadowSetup( pRenderable, pRenderable->GetBody(), pRenderable->GetSkin(), &info );
if ( pBoneToWorld )
{
modelrender->DrawModelShadow( pRenderable, info, pBoneToWorld );
}
bDrewTexture = true;
}
else if ( bDrawBrushShadow )
{
render->DrawBrushModelShadow( pRenderable );
bDrewTexture = true;
}
}


//-----------------------------------------------------------------------------
if ( !pRenderable )
// Compute a separating axis...
return bDrewTexture;
//-----------------------------------------------------------------------------
 
bool CClientShadowMgr::ComputeSeparatingPlane( IClientRenderable* pRend1, IClientRenderable* pRend2, cplane_t* pPlane )
IClientRenderable *pChild;
{
for ( pChild = pRenderable->FirstShadowChild(); pChild; pChild = pChild->NextShadowPeer() )
Vector min1, max1, min2, max2;
{
pRend1->GetShadowRenderBounds( min1, max1, GetActualShadowCastType( pRend1 ) );
if ( DrawShadowHierarchy( pChild, shadow, true ) )
pRend2->GetShadowRenderBounds( min2, max2, GetActualShadowCastType( pRend2 ) );
{
return ::ComputeSeparatingPlane(
bDrewTexture = true;
pRend1->GetRenderOrigin(), pRend1->GetRenderAngles(), min1, max1,
}
pRend2->GetRenderOrigin(), pRend2->GetRenderAngles(), min2, max2,
}
3.0f, pPlane );
return bDrewTexture;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Cull shadows based on rough bounding volumes
// This gets called with every shadow that potentially will need to re-render
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CClientShadowMgr::CullReceiver( ClientShadowHandle_t handle, IClientRenderable* pRenderable,
bool CClientShadowMgr::BuildSetupListForRenderToTextureShadow( unsigned short clientShadowHandle, float flArea )
IClientRenderable* pSourceRenderable )
{
{
// check flags here instead and assert !pSourceRenderable
ClientShadow_t& shadow = m_Shadows[clientShadowHandle];
if( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT )
bool bDirtyTexture = (shadow.m_Flags & SHADOW_FLAGS_TEXTURE_DIRTY) != 0;
bool bNeedsRedraw = m_ShadowAllocator.UseTexture( shadow.m_ShadowTexture, bDirtyTexture, flArea );
if ( bNeedsRedraw || bDirtyTexture )
{
{
VPROF_BUDGET( "CClientShadowMgr::CullReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
shadow.m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;


Assert( !pSourceRenderable );
if ( !m_ShadowAllocator.HasValidTexture( shadow.m_ShadowTexture ) )
const Frustum_t &frustum = shadowmgr->GetFlashlightFrustum( m_Shadows[handle].m_ShadowHandle );
return false;


Vector mins, maxs;
// shadow to be redrawn; for now, we'll always do it.
pRenderable->GetRenderBoundsWorldspace( mins, maxs );
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );


return R_CullBox( mins, maxs, frustum );
if ( BuildSetupShadowHierarchy( pRenderable, shadow ) )
return true;
}
}
return false;
}


Assert( pSourceRenderable );
//-----------------------------------------------------------------------------
// Compute a bounding sphere for the renderable
// This gets called with every shadow that potentially will need to re-render
Vector origin;
//-----------------------------------------------------------------------------
float radius;
bool CClientShadowMgr::DrawRenderToTextureShadow( unsigned short clientShadowHandle, float flArea )
ComputeBoundingSphere( pRenderable, origin, radius );
{
ClientShadow_t& shadow = m_Shadows[clientShadowHandle];


// Transform the sphere center into the space of the shadow
// If we were previously using the LOD shadow, set the material
Vector localOrigin;
bool bPreviouslyUsingLODShadow = ( shadow.m_Flags & SHADOW_FLAGS_USING_LOD_SHADOW ) != 0;  
const ClientShadow_t& shadow = m_Shadows[handle];
shadow.m_Flags &= ~SHADOW_FLAGS_USING_LOD_SHADOW;
const ShadowInfo_t& info = shadowmgr->GetInfo( shadow.m_ShadowHandle );
if ( bPreviouslyUsingLODShadow )
Vector3DMultiplyPosition( shadow.m_WorldToShadow, origin, localOrigin );
{
shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_RenderShadow, m_RenderModelShadow, (void*)(uintp)clientShadowHandle );
}


// Compute a rough bounding box for the shadow (in shadow space)
// Mark texture as being used...
Vector shadowMin, shadowMax;
bool bDirtyTexture = (shadow.m_Flags & SHADOW_FLAGS_TEXTURE_DIRTY) != 0;
shadowMin.Init( -shadow.m_WorldSize.x * 0.5f, -shadow.m_WorldSize.y * 0.5f, 0 );
bool bDrewTexture = false;
shadowMax.Init( shadow.m_WorldSize.x * 0.5f, shadow.m_WorldSize.y * 0.5f, info.m_MaxDist );
bool bNeedsRedraw = ( !m_bThreaded && m_ShadowAllocator.UseTexture( shadow.m_ShadowTexture, bDirtyTexture, flArea ) );


// If the bounding sphere doesn't intersect with the shadow volume, cull
if ( !m_ShadowAllocator.HasValidTexture( shadow.m_ShadowTexture ) )
if (!IsBoxIntersectingSphere( shadowMin, shadowMax, localOrigin, radius ))
return true;
 
Vector originSource;
float radiusSource;
ComputeBoundingSphere( pSourceRenderable, originSource, radiusSource );
 
// Fast check for separating plane...
bool foundSeparatingPlane = false;
cplane_t plane;
if (!IsSphereIntersectingSphere( originSource, radiusSource, origin, radius ))
{
{
foundSeparatingPlane = true;
DrawRenderToTextureShadowLOD( clientShadowHandle );
 
return false;
// the plane normal doesn't need to be normalized...
VectorSubtract( origin, originSource, plane.normal );
}
else
{
foundSeparatingPlane = ComputeSeparatingPlane( pRenderable, pSourceRenderable, &plane );
}
}


if (foundSeparatingPlane)
if ( bNeedsRedraw || bDirtyTexture )
{
{
// Compute which side of the plane the renderable is on..
// shadow to be redrawn; for now, we'll always do it.
    Vector vecShadowDir = GetShadowDirection( handle );
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
float shadowDot = DotProduct( vecShadowDir, plane.normal );
float receiverDot = DotProduct( plane.normal, origin );
float sourceDot = DotProduct( plane.normal, originSource );


if (shadowDot > 0.0f)
CMatRenderContextPtr pRenderContext( materials );
{
if (receiverDot <= sourceDot)
// Sets the viewport state
int x, y, w, h;
m_ShadowAllocator.GetTextureRect( shadow.m_ShadowTexture, x, y, w, h );
pRenderContext->Viewport( IsX360() ? 0 : x, IsX360() ? 0 : y, w, h );
 
// Clear the selected viewport only (don't need to clear depth)
pRenderContext->ClearBuffers( true, false );
 
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->LoadMatrix( shadowmgr->GetInfo( shadow.m_ShadowHandle ).m_WorldToShadow );
 
if ( DrawShadowHierarchy( pRenderable, shadow ) )
{
bDrewTexture = true;
if ( IsX360() )
{
{
// Vector dest;
// resolve render target to system memory texture
// VectorMA( pSourceRenderable->GetRenderOrigin(), 50, plane.normal, dest );
Rect_t srcRect = { 0, 0, w, h };
// debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 255, 0, true, 1.0f );
Rect_t dstRect = { x, y, w, h };
return true;
pRenderContext->CopyRenderTargetToTextureEx( m_ShadowAllocator.GetTexture(), 0, &srcRect, &dstRect );
}
else
{
// Vector dest;
// VectorMA( pSourceRenderable->GetRenderOrigin(), 50, plane.normal, dest );  
// debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 0, 0, true, 1.0f );
}
}
}
}
else
else
{
{
if (receiverDot >= sourceDot)
// NOTE: Think the flags reset + texcoord set should only happen in DrawShadowHierarchy
{
// but it's 2 days before 360 ship.. not going to change this now.
// Vector dest;
DevMsg( "Didn't draw shadow hierarchy.. bad shadow texcoords probably going to happen..grab Brian!\n" );
// VectorMA( pSourceRenderable->GetRenderOrigin(), -50, plane.normal, dest );
}
// debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 255, 0, true, 1.0f );
 
return true;
// Only clear the dirty flag if the caster isn't animating
}
if ( (shadow.m_Flags & SHADOW_FLAGS_ANIMATING_SOURCE) == 0 )
else
{
{
shadow.m_Flags &= ~SHADOW_FLAGS_TEXTURE_DIRTY;
// Vector dest;
// VectorMA( pSourceRenderable->GetRenderOrigin(), 50, plane.normal, dest );
// debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 0, 0, true, 1.0f );
}
}
}
SetRenderToTextureShadowTexCoords( shadow.m_ShadowHandle, x, y, w, h );
}
else if ( bPreviouslyUsingLODShadow )
{
// In this case, we were previously using the LOD shadow, but we didn't
// have to reconstitute the texture. In this case, we need to reset the texcoord
int x, y, w, h;
m_ShadowAllocator.GetTextureRect( shadow.m_ShadowTexture, x, y, w, h );
SetRenderToTextureShadowTexCoords( shadow.m_ShadowHandle, x, y, w, h );
}
}


// No additional clip planes? ok then it's a valid receiver
return bDrewTexture;
/*
}
if (shadow.m_ClipPlaneCount == 0)
return false;


// Check the additional cull planes
//-----------------------------------------------------------------------------
int i;
// "Draws" the shadow LOD, which really means just set up the blobby shadow
for ( i = 0; i < shadow.m_ClipPlaneCount; ++i)
//-----------------------------------------------------------------------------
void CClientShadowMgr::DrawRenderToTextureShadowLOD( unsigned short clientShadowHandle )
{
ClientShadow_t &shadow = m_Shadows[clientShadowHandle];
if ( (shadow.m_Flags & SHADOW_FLAGS_USING_LOD_SHADOW) == 0 )
{
{
// Fast sphere cull
shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_SimpleShadow, m_SimpleShadow, (void*)CLIENTSHADOW_INVALID_HANDLE );
if (DotProduct( origin, shadow.m_ClipPlane[i] ) - radius > shadow.m_ClipDist[i])
shadowmgr->SetShadowTexCoord( shadow.m_ShadowHandle, 0, 0, 1, 1 );
return true;
ClearExtraClipPlanes( clientShadowHandle ); // this was ClearExtraClipPlanes( shadow.m_ShadowHandle ), fix is from Joe Demers
shadow.m_Flags |= SHADOW_FLAGS_USING_LOD_SHADOW;
}
}
}


// More expensive box on plane side cull...
//-----------------------------------------------------------------------------
Vector vec[3];
// Advances to the next frame,  
Vector mins, maxs;
//-----------------------------------------------------------------------------
cplane_t plane;
void CClientShadowMgr::AdvanceFrame()
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
{
pRenderable->GetBounds( mins, maxs );
// We're starting the next frame
m_ShadowAllocator.AdvanceFrame();
}


for ( i = 0; i < shadow.m_ClipPlaneCount; ++i)
//-----------------------------------------------------------------------------
// Re-render shadow depth textures that lie in the leaf list
//-----------------------------------------------------------------------------
int CClientShadowMgr::BuildActiveShadowDepthList( const CViewSetup &viewSetup, int nMaxDepthShadows, ClientShadowHandle_t *pActiveDepthShadows )
{
int nActiveDepthShadowCount = 0;
for ( ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
{
{
// Transform the plane into the space of the receiver
ClientShadow_t& shadow = m_Shadows[i];
plane.normal.x = DotProduct( vec[0], shadow.m_ClipPlane[i] );
plane.normal.y = DotProduct( vec[1], shadow.m_ClipPlane[i] );
plane.normal.z = DotProduct( vec[2], shadow.m_ClipPlane[i] );


plane.dist = shadow.m_ClipDist[i] - DotProduct( shadow.m_ClipPlane[i], pRenderable->GetRenderOrigin() );
// If this is not a flashlight which should use a shadow depth texture, skip!
if ( ( shadow.m_Flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE ) == 0 )
continue;


// If the box is on the front side of the plane, we're done.
const FlashlightState_t& flashlightState = shadowmgr->GetFlashlightState( shadow.m_ShadowHandle );
if (BoxOnPlaneSide2( mins, maxs, &plane, 3.0f ) == 1)
return true;
}
*/


return false;
// Bail if this flashlight doesn't want shadows
}
if ( !flashlightState.m_bEnableShadows )
continue;


// Calculate an AABB around the shadow frustum
Vector vecAbsMins, vecAbsMaxs;
CalculateAABBFromProjectionMatrix( shadow.m_WorldToShadow, &vecAbsMins, &vecAbsMaxs );


//-----------------------------------------------------------------------------
Frustum_t viewFrustum;
// deals with shadows being added to shadow receivers
GeneratePerspectiveFrustum( viewSetup.origin, viewSetup.angles, viewSetup.zNear, viewSetup.zFar, viewSetup.fov, viewSetup.m_flAspectRatio, viewFrustum );
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddShadowToReceiver( ClientShadowHandle_t handle,
IClientRenderable* pRenderable, ShadowReceiver_t type )
{
ClientShadow_t &shadow = m_Shadows[handle];


// Don't add a shadow cast by an object to itself...
// FIXME: Could do other sorts of culling here, such as frustum-frustum test, distance etc.
IClientRenderable* pSourceRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
// If it's not in the view frustum, move on
if ( R_CullBox( vecAbsMins, vecAbsMaxs, viewFrustum ) )
{
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
continue;
}


// NOTE: if pSourceRenderable == NULL, the source is probably a flashlight since there is no entity.
if ( nActiveDepthShadowCount >= nMaxDepthShadows )
if (pSourceRenderable == pRenderable)
{
return;
static bool s_bOverflowWarning = false;
if ( !s_bOverflowWarning )
{
Warning( "Too many depth textures rendered in a single view!\n" );
Assert( 0 );
s_bOverflowWarning = true;
}
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
continue;
}


// Don't bother if this renderable doesn't receive shadows or light from flashlights
pActiveDepthShadows[nActiveDepthShadowCount++] = i;
if( !pRenderable->ShouldReceiveProjectedTextures( SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ) )
}
return;
return nActiveDepthShadowCount;
}


// Cull if the origin is on the wrong side of a shadow clip plane....
//-----------------------------------------------------------------------------
if ( CullReceiver( handle, pRenderable, pSourceRenderable ) )
// Sets the view's active flashlight render state
return;
//-----------------------------------------------------------------------------
 
void CClientShadowMgr::SetViewFlashlightState( int nActiveFlashlightCount, ClientShadowHandle_t* pActiveFlashlights )
// Do different things depending on the receiver type
{
switch( type )
// NOTE: On the 360, we render the entire scene with the flashlight state
// set and don't render flashlights additively in the shadow mgr at a far later time
// because the CPU costs are prohibitive
if ( !IsX360() && !r_flashlight_version2.GetInt() )
return;
 
Assert( nActiveFlashlightCount<= 1 );
if ( nActiveFlashlightCount > 0 )
{
Assert( ( m_Shadows[ pActiveFlashlights[0] ].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) != 0 );
shadowmgr->SetFlashlightRenderState( pActiveFlashlights[0] );
}
else
{
{
case SHADOW_RECEIVER_BRUSH_MODEL:
shadowmgr->SetFlashlightRenderState( SHADOW_HANDLE_INVALID );
}
}


if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
//-----------------------------------------------------------------------------
{
// Re-render shadow depth textures that lie in the leaf list
VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeShadowDepthTextures( const CViewSetup &viewSetup )
{
VPROF_BUDGET( "CClientShadowMgr::ComputeShadowDepthTextures", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );


if( (!shadow.m_hTargetEntity) || IsFlashlightTarget( handle, pRenderable ) )
CMatRenderContextPtr pRenderContext( materials );
{
PIXEVENT( pRenderContext, "Shadow Depth Textures" );
shadowmgr->AddShadowToBrushModel( shadow.m_ShadowHandle,
const_cast<model_t*>(pRenderable->GetModel()),
pRenderable->GetRenderOrigin(), pRenderable->GetRenderAngles() );


shadowmgr->AddFlashlightRenderable( shadow.m_ShadowHandle, pRenderable );
// Build list of active render-to-texture shadows
}
ClientShadowHandle_t pActiveDepthShadows[1024];
}
int nActiveDepthShadowCount = BuildActiveShadowDepthList( viewSetup, ARRAYSIZE( pActiveDepthShadows ), pActiveDepthShadows );
else
 
{
// Iterate over all existing textures and allocate shadow textures
shadowmgr->AddShadowToBrushModel( shadow.m_ShadowHandle,
bool bDebugFrustum = r_flashlightdrawfrustum.GetBool();
const_cast<model_t*>(pRenderable->GetModel()),
for ( int j = 0; j < nActiveDepthShadowCount; ++j )
pRenderable->GetRenderOrigin(), pRenderable->GetRenderAngles() );
{
}
ClientShadow_t& shadow = m_Shadows[ pActiveDepthShadows[j] ];
break;


case SHADOW_RECEIVER_STATIC_PROP:
CTextureReference shadowDepthTexture;
// Don't add shadows to props if we're not using render-to-texture
bool bGotShadowDepthTexture = LockShadowDepthTexture( &shadowDepthTexture );
if ( GetActualShadowCastType( handle ) == SHADOWS_RENDER_TO_TEXTURE )
if ( !bGotShadowDepthTexture )
{
{
// Also don't add them unless an NPC or player casts them..
// If we don't get one, that means we have too many this frame so bind no depth texture
// They are wickedly expensive!!!
static int bitchCount = 0;
C_BaseEntity *pEnt = pSourceRenderable->GetIClientUnknown()->GetBaseEntity();
if( bitchCount < 10 )
if ( pEnt && ( pEnt->GetFlags() & (FL_NPC | FL_CLIENT)) )
{
{
staticpropmgr->AddShadowToStaticProp( shadow.m_ShadowHandle, pRenderable );
Warning( "Too many shadow maps this frame!\n"  );
bitchCount++;
}
}
}
else if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
{
VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );


if( (!shadow.m_hTargetEntity) || IsFlashlightTarget( handle, pRenderable ) )
Assert(0);
{
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
staticpropmgr->AddShadowToStaticProp( shadow.m_ShadowHandle, pRenderable );
continue;
 
shadowmgr->AddFlashlightRenderable( shadow.m_ShadowHandle, pRenderable );
}
}
}
break;


case SHADOW_RECEIVER_STUDIO_MODEL:
CViewSetup shadowView;
if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
shadowView.m_flAspectRatio = 1.0f;
shadowView.x = shadowView.y = 0;
shadowView.width = shadowDepthTexture->GetActualWidth();
shadowView.height = shadowDepthTexture->GetActualHeight();
shadowView.m_bOrtho = false;
shadowView.m_bDoBloomAndToneMapping = false;
 
// Copy flashlight parameters
const FlashlightState_t& flashlightState = shadowmgr->GetFlashlightState( shadow.m_ShadowHandle );
shadowView.fov = shadowView.fovViewmodel = flashlightState.m_fHorizontalFOVDegrees;
shadowView.origin = flashlightState.m_vecLightOrigin;
QuaternionAngles( flashlightState.m_quatOrientation, shadowView.angles ); // Convert from Quaternion to QAngle
 
shadowView.zNear = shadowView.zNearViewmodel = flashlightState.m_NearZ;
shadowView.zFar = shadowView.zFarViewmodel = flashlightState.m_FarZ;
 
// Can turn on all light frustum overlays or per light with flashlightState parameter...
if ( bDebugFrustum || flashlightState.m_bDrawShadowFrustum )
{
{
VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
DebugDrawFrustum( shadowView.origin, shadow.m_WorldToShadow );
}
 
// Set depth bias factors specific to this flashlight
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->SetShadowDepthBiasFactors( flashlightState.m_flShadowSlopeScaleDepthBias, flashlightState.m_flShadowDepthBias );
 
// Render to the shadow depth texture with appropriate view
view->UpdateShadowDepthTexture( m_DummyColorTexture, shadowDepthTexture, shadowView );


if( (!shadow.m_hTargetEntity) || IsFlashlightTarget( handle, pRenderable ) )
// Associate the shadow depth texture and stencil bit with the flashlight for use during scene rendering
{
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, shadowDepthTexture, 0 );
pRenderable->CreateModelInstance();
shadowmgr->AddShadowToModel( shadow.m_ShadowHandle, pRenderable->GetModelInstance() );
shadowmgr->AddFlashlightRenderable( shadow.m_ShadowHandle, pRenderable );
}
}
break;
// default:
}
}
SetViewFlashlightState( nActiveDepthShadowCount, pActiveDepthShadows );
}
}


 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// deals with shadows being added to shadow receivers
// Re-renders all shadow textures for shadow casters that lie in the leaf list
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::RemoveAllShadowsFromReceiver(  
static void SetupBonesOnBaseAnimating( C_BaseAnimating *&pBaseAnimating )
IClientRenderable* pRenderable, ShadowReceiver_t type )
{
pBaseAnimating->SetupBones( NULL, -1, -1, gpGlobals->curtime );
}
 
void CClientShadowMgr::ComputeShadowTextures( const CViewSetup &view, int leafCount, LeafIndex_t* pLeafList )
{
{
// Don't bother if this renderable doesn't receive shadows
VPROF_BUDGET( "CClientShadowMgr::ComputeShadowTextures", VPROF_BUDGETGROUP_SHADOW_RENDERING );
if ( !pRenderable->ShouldReceiveProjectedTextures( SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ) )
 
if ( !m_RenderToTextureActive || (r_shadows.GetInt() == 0) || r_shadows_gamecontrol.GetInt() == 0 )
return;
return;


// Do different things depending on the receiver type
m_bThreaded = false;//( r_threaded_client_shadow_manager.GetBool() && g_pThreadPool->NumIdleThreads() );
switch( type )
{
case SHADOW_RECEIVER_BRUSH_MODEL:
{
model_t* pModel = const_cast<model_t*>(pRenderable->GetModel());
shadowmgr->RemoveAllShadowsFromBrushModel( pModel );
}
break;


case SHADOW_RECEIVER_STATIC_PROP:
MDLCACHE_CRITICAL_SECTION();
staticpropmgr->RemoveAllShadowsFromStaticProp(pRenderable);
// First grab all shadow textures we may want to render
break;
int nCount = s_VisibleShadowList.FindShadows( &view, leafCount, pLeafList );
if ( nCount == 0 )
return;


case SHADOW_RECEIVER_STUDIO_MODEL:
// FIXME: Add heuristics based on distance, etc. to futz with
if( pRenderable && pRenderable->GetModelInstance() != MODEL_INSTANCE_INVALID )
// the shadow allocator + to select blobby shadows
{
shadowmgr->RemoveAllShadowsFromModel( pRenderable->GetModelInstance() );
}
break;


// default:
CMatRenderContextPtr pRenderContext( materials );
// // FIXME: How do deal with this stuff? Add a method to IClientRenderable?
// C_BaseEntity* pEnt = static_cast<C_BaseEntity*>(pRenderable);
// pEnt->RemoveAllShadows();
}
}


PIXEVENT( pRenderContext, "Render-To-Texture Shadows" );


//-----------------------------------------------------------------------------
// Clear to white (color unused), black alpha
// Computes + sets the render-to-texture texcoords
pRenderContext->ClearColor4ub( 255, 255, 255, 0 );
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetRenderToTextureShadowTexCoords( ShadowHandle_t handle, int x, int y, int w, int h )
{
// Let the shadow mgr know about the texture coordinates...
// That way it'll be able to batch rendering better.
int textureW, textureH;
m_ShadowAllocator.GetTotalTextureSize( textureW, textureH );


// Go in a half-pixel to avoid blending with neighboring textures..
// No height clip mode please.
float u, v, du, dv;
MaterialHeightClipMode_t oldHeightClipMode = pRenderContext->GetHeightClipMode();
pRenderContext->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE );


u  = ((float)x + 0.5f) / (float)textureW;
// No projection matrix (orthographic mode)
v  = ((float)y + 0.5f) / (float)textureH;
// FIXME: Make it work for projective shadows?
du = ((float)w - 1) / (float)textureW;
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
dv = ((float)h - 1) / (float)textureH;
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->Scale( 1, -1, 1 );
pRenderContext->Ortho( 0, 0, 1, 1, -9999, 0 );


shadowmgr->SetShadowTexCoord( handle, u, v, du, dv );
pRenderContext->MatrixMode( MATERIAL_VIEW );
}
pRenderContext->PushMatrix();


pRenderContext->PushRenderTargetAndViewport( m_ShadowAllocator.GetTexture() );


//-----------------------------------------------------------------------------
if ( !IsX360() && m_bRenderTargetNeedsClear )
// Setup all children shadows
{
//-----------------------------------------------------------------------------
// don't need to clear absent depth buffer
bool CClientShadowMgr::BuildSetupShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild )
pRenderContext->ClearBuffers( true, false );
{
m_bRenderTargetNeedsClear = false;
bool bDrewTexture = false;
}


// Stop traversing when we hit a blobby shadow
int nMaxShadows = r_shadowmaxrendered.GetInt();
ShadowType_t shadowType = GetActualShadowCastType( pRenderable );
int nModelsRendered = 0;
if ( pRenderable && shadowType == SHADOWS_SIMPLE )
int i;
return false;


if ( !pRenderable || shadowType != SHADOWS_NONE )
if ( m_bThreaded && g_pThreadPool->NumIdleThreads() )
{
{
bool bDrawModelShadow;
s_NPCShadowBoneSetups.RemoveAll();
if ( !bChild )
s_NonNPCShadowBoneSetups.RemoveAll();
{
bDrawModelShadow = ((shadow.m_Flags & SHADOW_FLAGS_BRUSH_MODEL) == 0);
}
else
{
int nModelType = modelinfo->GetModelType( pRenderable->GetModel() );
bDrawModelShadow = nModelType == mod_studio;
}


if ( bDrawModelShadow )
for (i = 0; i < nCount; ++i)
{
{
C_BaseEntity *pEntity = pRenderable->GetIClientUnknown()->GetBaseEntity();
const VisibleShadowInfo_t &info = s_VisibleShadowList.GetVisibleShadow(i);
if ( pEntity )
if ( nModelsRendered < nMaxShadows )
{
{
if ( pEntity->IsNPC() )
if ( BuildSetupListForRenderToTextureShadow( info.m_hShadow, info.m_flArea ) )
{
{
s_NPCShadowBoneSetups.AddToTail( assert_cast<C_BaseAnimating *>( pEntity ) );
++nModelsRendered;
}
}
else if ( pEntity->GetBaseAnimating() )
{
s_NonNPCShadowBoneSetups.AddToTail( assert_cast<C_BaseAnimating *>( pEntity ) );
}
}
}
bDrewTexture = true;
}
}
}


if ( !pRenderable )
ParallelProcess( "NPCShadowBoneSetups", s_NPCShadowBoneSetups.Base(), s_NPCShadowBoneSetups.Count(), &SetupBonesOnBaseAnimating );
return bDrewTexture;
ParallelProcess( "NonNPCShadowBoneSetups", s_NonNPCShadowBoneSetups.Base(), s_NonNPCShadowBoneSetups.Count(), &SetupBonesOnBaseAnimating );


IClientRenderable *pChild;
nModelsRendered = 0;
for ( pChild = pRenderable->FirstShadowChild(); pChild; pChild = pChild->NextShadowPeer() )
{
if ( BuildSetupShadowHierarchy( pChild, shadow, true ) )
{
bDrewTexture = true;
}
}
}
return bDrewTexture;
}


//-----------------------------------------------------------------------------
for (i = 0; i < nCount; ++i)
// Draws all children shadows into our own
//-----------------------------------------------------------------------------
bool CClientShadowMgr::DrawShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild )
{
bool bDrewTexture = false;
 
// Stop traversing when we hit a blobby shadow
ShadowType_t shadowType = GetActualShadowCastType( pRenderable );
if ( pRenderable && shadowType == SHADOWS_SIMPLE )
return false;
 
if ( !pRenderable || shadowType != SHADOWS_NONE )
{
{
bool bDrawModelShadow;
const VisibleShadowInfo_t &info = s_VisibleShadowList.GetVisibleShadow(i);
bool bDrawBrushShadow;
if ( nModelsRendered < nMaxShadows )
if ( !bChild )
{
{
bDrawModelShadow = ((shadow.m_Flags & SHADOW_FLAGS_BRUSH_MODEL) == 0);
if ( DrawRenderToTextureShadow( info.m_hShadow, info.m_flArea ) )
bDrawBrushShadow = !bDrawModelShadow;
{
++nModelsRendered;
}
}
}
else
else
{
{
int nModelType = modelinfo->GetModelType( pRenderable->GetModel() );
DrawRenderToTextureShadowLOD( info.m_hShadow );
bDrawModelShadow = nModelType == mod_studio;
bDrawBrushShadow = nModelType == mod_brush;
}
}
   
}
if ( bDrawModelShadow )
 
{
// Render to the backbuffer again
DrawModelInfo_t info;
pRenderContext->PopRenderTargetAndViewport();
matrix3x4_t *pBoneToWorld = modelrender->DrawModelShadowSetup( pRenderable, pRenderable->GetBody(), pRenderable->GetSkin(), &info );
 
if ( pBoneToWorld )
// Restore the matrices
{
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
modelrender->DrawModelShadow( pRenderable, info, pBoneToWorld );
pRenderContext->PopMatrix();
}
 
bDrewTexture = true;
pRenderContext->MatrixMode( MATERIAL_VIEW );
}
pRenderContext->PopMatrix();
else if ( bDrawBrushShadow )
 
{
pRenderContext->SetHeightClipMode( oldHeightClipMode );
render->DrawBrushModelShadow( pRenderable );
bDrewTexture = true;
}
}


if ( !pRenderable )
pRenderContext->SetHeightClipMode( oldHeightClipMode );
return bDrewTexture;


IClientRenderable *pChild;
// Restore the clear color
for ( pChild = pRenderable->FirstShadowChild(); pChild; pChild = pChild->NextShadowPeer() )
pRenderContext->ClearColor3ub( 0, 0, 0 );
{
if ( DrawShadowHierarchy( pChild, shadow, true ) )
{
bDrewTexture = true;
}
}
return bDrewTexture;
}
}


//-----------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------
// This gets called with every shadow that potentially will need to re-render
// Lock down the usage of a shadow depth texture...must be unlocked for use on subsequent views / frames
//-----------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------
bool CClientShadowMgr::BuildSetupListForRenderToTextureShadow( unsigned short clientShadowHandle, float flArea )
bool CClientShadowMgr::LockShadowDepthTexture( CTextureReference *shadowDepthTexture )
{
{
ClientShadow_t& shadow = m_Shadows[clientShadowHandle];
for ( int i=0; i < m_DepthTextureCache.Count(); i++ ) // Search for cached shadow depth texture
bool bDirtyTexture = (shadow.m_Flags & SHADOW_FLAGS_TEXTURE_DIRTY) != 0;
bool bNeedsRedraw = m_ShadowAllocator.UseTexture( shadow.m_ShadowTexture, bDirtyTexture, flArea );
if ( bNeedsRedraw || bDirtyTexture )
{
{
shadow.m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;
if ( m_DepthTextureCacheLocks[i] == false ) // If a free one is found
 
{
if ( !m_ShadowAllocator.HasValidTexture( shadow.m_ShadowTexture ) )
*shadowDepthTexture = m_DepthTextureCache[i];
return false;
m_DepthTextureCacheLocks[i] = true;
 
// shadow to be redrawn; for now, we'll always do it.
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
 
if ( BuildSetupShadowHierarchy( pRenderable, shadow ) )
return true;
return true;
}
}
}
return false;
 
return false; // Didn't find it...
}
}


//-----------------------------------------------------------------------------
//------------------------------------------------------------------
// This gets called with every shadow that potentially will need to re-render
// Unlock shadow depth texture for use on subsequent views / frames
//-----------------------------------------------------------------------------
//------------------------------------------------------------------
bool CClientShadowMgr::DrawRenderToTextureShadow( unsigned short clientShadowHandle, float flArea )
void CClientShadowMgr::UnlockAllShadowDepthTextures()
{
{
ClientShadow_t& shadow = m_Shadows[clientShadowHandle];
for (int i=0; i< m_DepthTextureCache.Count(); i++ )
{
m_DepthTextureCacheLocks[i] = false;
}
SetViewFlashlightState( 0, NULL );
}


// If we were previously using the LOD shadow, set the material
void CClientShadowMgr::SetFlashlightTarget( ClientShadowHandle_t shadowHandle, EHANDLE targetEntity )
bool bPreviouslyUsingLODShadow = ( shadow.m_Flags & SHADOW_FLAGS_USING_LOD_SHADOW ) != 0;  
{
shadow.m_Flags &= ~SHADOW_FLAGS_USING_LOD_SHADOW;
Assert( m_Shadows.IsValidIndex( shadowHandle ) );
if ( bPreviouslyUsingLODShadow )
 
{
CClientShadowMgr::ClientShadow_t &shadow = m_Shadows[ shadowHandle ];
shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_RenderShadow, m_RenderModelShadow, (void*)(uintp)clientShadowHandle );
if( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 )
}
return;
 
// shadow.m_pTargetRenderable = pRenderable;
shadow.m_hTargetEntity = targetEntity;
}
 
void CClientShadowMgr::SetFlashlightLightWorld( ClientShadowHandle_t shadowHandle, bool bLightWorld )
{
Assert( m_Shadows.IsValidIndex( shadowHandle ) );


// Mark texture as being used...
ClientShadow_t &shadow = m_Shadows[ shadowHandle ];
bool bDirtyTexture = (shadow.m_Flags & SHADOW_FLAGS_TEXTURE_DIRTY) != 0;
if( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 )
bool bDrewTexture = false;
return;
bool bNeedsRedraw = ( !m_bThreaded && m_ShadowAllocator.UseTexture( shadow.m_ShadowTexture, bDirtyTexture, flArea ) );


if ( !m_ShadowAllocator.HasValidTexture( shadow.m_ShadowTexture ) )
if ( bLightWorld )
{
{
DrawRenderToTextureShadowLOD( clientShadowHandle );
shadow.m_Flags |= SHADOW_FLAGS_LIGHT_WORLD;
return false;
}
}
 
else
if ( bNeedsRedraw || bDirtyTexture )
{
{
// shadow to be redrawn; for now, we'll always do it.
shadow.m_Flags &= ~SHADOW_FLAGS_LIGHT_WORLD;
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
}
}


CMatRenderContextPtr pRenderContext( materials );
bool CClientShadowMgr::IsFlashlightTarget( ClientShadowHandle_t shadowHandle, IClientRenderable *pRenderable )
{
// Sets the viewport state
ClientShadow_t &shadow = m_Shadows[ shadowHandle ];
int x, y, w, h;
m_ShadowAllocator.GetTextureRect( shadow.m_ShadowTexture, x, y, w, h );
pRenderContext->Viewport( IsX360() ? 0 : x, IsX360() ? 0 : y, w, h );  


// Clear the selected viewport only (don't need to clear depth)
if( shadow.m_hTargetEntity->GetClientRenderable() == pRenderable )
pRenderContext->ClearBuffers( true, false );
return true;


pRenderContext->MatrixMode( MATERIAL_VIEW );
C_BaseEntity *pChild = shadow.m_hTargetEntity->FirstMoveChild();
pRenderContext->LoadMatrix( shadowmgr->GetInfo( shadow.m_ShadowHandle ).m_WorldToShadow );
while( pChild )
 
{
if ( DrawShadowHierarchy( pRenderable, shadow ) )
if( pChild->GetClientRenderable()==pRenderable )
{
return true;
bDrewTexture = true;
if ( IsX360() )
{
// resolve render target to system memory texture
Rect_t srcRect = { 0, 0, w, h };
Rect_t dstRect = { x, y, w, h };
pRenderContext->CopyRenderTargetToTextureEx( m_ShadowAllocator.GetTexture(), 0, &srcRect, &dstRect );
}
}
else
{
// NOTE: Think the flags reset + texcoord set should only happen in DrawShadowHierarchy
// but it's 2 days before 360 ship.. not going to change this now.
DevMsg( "Didn't draw shadow hierarchy.. bad shadow texcoords probably going to happen..grab Brian!\n" );
}


// Only clear the dirty flag if the caster isn't animating
pChild = pChild->NextMovePeer();
if ( (shadow.m_Flags & SHADOW_FLAGS_ANIMATING_SOURCE) == 0 )
{
shadow.m_Flags &= ~SHADOW_FLAGS_TEXTURE_DIRTY;
}
 
SetRenderToTextureShadowTexCoords( shadow.m_ShadowHandle, x, y, w, h );
}
}
else if ( bPreviouslyUsingLODShadow )
{
return false;
// In this case, we were previously using the LOD shadow, but we didn't
// have to reconstitute the texture. In this case, we need to reset the texcoord
int x, y, w, h;
m_ShadowAllocator.GetTextureRect( shadow.m_ShadowTexture, x, y, w, h );
SetRenderToTextureShadowTexCoords( shadow.m_ShadowHandle, x, y, w, h );
}
 
return bDrewTexture;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// "Draws" the shadow LOD, which really means just set up the blobby shadow
// Purpose:
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::DrawRenderToTextureShadowLOD( unsigned short clientShadowHandle )
const Vector &CClientShadowMgr::GetShadowDirection( ClientShadowHandle_t shadowHandle ) const
{
{
ClientShadow_t &shadow = m_Shadows[clientShadowHandle];
Assert( shadowHandle != CLIENTSHADOW_INVALID_HANDLE );
if ( (shadow.m_Flags & SHADOW_FLAGS_USING_LOD_SHADOW) == 0 )
 
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[shadowHandle].m_Entity );
Assert( pRenderable );
 
if ( !IsShadowingFromWorldLights() )
{
{
shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_SimpleShadow, m_SimpleShadow, (void*)CLIENTSHADOW_INVALID_HANDLE );
return GetShadowDirection( pRenderable );
shadowmgr->SetShadowTexCoord( shadow.m_ShadowHandle, 0, 0, 1, 1 );
ClearExtraClipPlanes( clientShadowHandle ); // this was ClearExtraClipPlanes( shadow.m_ShadowHandle ), fix is from Joe Demers
shadow.m_Flags |= SHADOW_FLAGS_USING_LOD_SHADOW;
}
}
Vector &vecResult = AllocTempVector();
vecResult = m_Shadows[shadowHandle].m_ShadowDir;
// Allow the renderable to override the default
pRenderable->GetShadowCastDirection( &vecResult, GetActualShadowCastType( pRenderable ) );
return vecResult;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Advances to the next frame,
// Purpose:
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::AdvanceFrame()
void CClientShadowMgr::UpdateShadowDirectionFromLocalLightSource( ClientShadowHandle_t shadowHandle )
{
{
// We're starting the next frame
Assert( shadowHandle != CLIENTSHADOW_INVALID_HANDLE );
m_ShadowAllocator.AdvanceFrame();
 
}
ClientShadow_t &shadow = m_Shadows[shadowHandle];


IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );


//-----------------------------------------------------------------------------
// TODO: Figure out why this still gets hit
// Re-render shadow depth textures that lie in the leaf list
Assert( pRenderable );
//-----------------------------------------------------------------------------
if ( !pRenderable )
int CClientShadowMgr::BuildActiveShadowDepthList( const CViewSetup &viewSetup, int nMaxDepthShadows, ClientShadowHandle_t *pActiveDepthShadows )
{
int nActiveDepthShadowCount = 0;
for ( ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
{
{
ClientShadow_t& shadow = m_Shadows[i];
DevWarning( "%s(): Skipping shadow with invalid client renderable (shadow handle %d)\n", __FUNCTION__, shadowHandle );
return;
}


// If this is not a flashlight which should use a shadow depth texture, skip!
Vector bbMin, bbMax;
if ( ( shadow.m_Flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE ) == 0 )
pRenderable->GetRenderBoundsWorldspace( bbMin, bbMax );
continue;
Vector origin( 0.5f * ( bbMin + bbMax ) );
origin.z = bbMin.z; // Putting origin at the bottom of the bounding box makes the shadows a little shorter
Vector lightPos;
Vector lightBrightness;


const FlashlightState_t& flashlightState = shadowmgr->GetFlashlightState( shadow.m_ShadowHandle );
if ( shadow.m_LightPosLerp >= 1.0f ) // Skip finding new light source if we're in the middle of a lerp
{
// Calculate minimum brightness squared
float flMinBrightnessSqr = r_worldlight_mincastintensity.GetFloat();
flMinBrightnessSqr *= flMinBrightnessSqr;


// Bail if this flashlight doesn't want shadows
if ( g_pWorldLights->GetBrightestLightSource( pRenderable->GetRenderOrigin(), lightPos, lightBrightness ) == false || lightBrightness.LengthSqr() < flMinBrightnessSqr )
if ( !flashlightState.m_bEnableShadows )
{
continue;
// Didn't find a light source at all, use default shadow direction
// TODO: Could switch to using blobby shadow in this case
lightPos.Init( FLT_MAX, FLT_MAX, FLT_MAX );
}
}


// Calculate an AABB around the shadow frustum
if ( shadow.m_LightPosLerp == FLT_MAX ) // First light pos ever, just init
Vector vecAbsMins, vecAbsMaxs;
{
CalculateAABBFromProjectionMatrix( shadow.m_WorldToShadow, &vecAbsMins, &vecAbsMaxs );
shadow.m_CurrentLightPos = lightPos;
shadow.m_TargetLightPos = lightPos;
shadow.m_LightPosLerp = 1.0f;
}
else if ( shadow.m_LightPosLerp < 1.0f )
{
// We're in the middle of a lerp from current to target light. Finish it.
shadow.m_LightPosLerp += gpGlobals->frametime * 1.0f / r_worldlight_lerptime.GetFloat();
shadow.m_LightPosLerp = clamp( shadow.m_LightPosLerp, 0.0f, 1.0f );


Frustum_t viewFrustum;
Vector currLightPos( shadow.m_CurrentLightPos );
GeneratePerspectiveFrustum( viewSetup.origin, viewSetup.angles, viewSetup.zNear, viewSetup.zFar, viewSetup.fov, viewSetup.m_flAspectRatio, viewFrustum );
Vector targetLightPos( shadow.m_TargetLightPos );
 
if ( currLightPos.x == FLT_MAX )
// FIXME: Could do other sorts of culling here, such as frustum-frustum test, distance etc.
// If it's not in the view frustum, move on
if ( R_CullBox( vecAbsMins, vecAbsMaxs, viewFrustum ) )
{
{
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
currLightPos = origin - 200.0f * GetShadowDirection();
continue;
}
}
 
if ( targetLightPos.x == FLT_MAX )
if ( nActiveDepthShadowCount >= nMaxDepthShadows )
{
{
static bool s_bOverflowWarning = false;
targetLightPos = origin - 200.0f * GetShadowDirection();
if ( !s_bOverflowWarning )
{
Warning( "Too many depth textures rendered in a single view!\n" );
Assert( 0 );
s_bOverflowWarning = true;
}
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
continue;
}
}


pActiveDepthShadows[nActiveDepthShadowCount++] = i;
// Lerp light pos
}
Vector v1 = origin - shadow.m_CurrentLightPos;
return nActiveDepthShadowCount;
v1.NormalizeInPlace();
}


Vector v2 = origin - shadow.m_TargetLightPos;
v2.NormalizeInPlace();


//-----------------------------------------------------------------------------
// SAULUNDONE: Caused over top sweeping far too often
// Sets the view's active flashlight render state
#if 0
//-----------------------------------------------------------------------------
if ( v1.Dot( v2 ) < 0.0f )
void CClientShadowMgr::SetViewFlashlightState( int nActiveFlashlightCount, ClientShadowHandle_t* pActiveFlashlights )
{
{
// If change in shadow angle is more than 90 degrees, lerp over the renderable's top to avoid long sweeping shadows
// NOTE: On the 360, we render the entire scene with the flashlight state
Vector fakeOverheadLightPos( origin.x, origin.y, origin.z + 200.0f );
// set and don't render flashlights additively in the shadow mgr at a far later time
if ( shadow.m_LightPosLerp < 0.5f )
// because the CPU costs are prohibitive
{
if ( !IsX360() && !r_flashlight_version2.GetInt() )
lightPos = Lerp( 2.0f * shadow.m_LightPosLerp, currLightPos, fakeOverheadLightPos );
return;
}
else
{
lightPos = Lerp( 2.0f * shadow.m_LightPosLerp - 1.0f, fakeOverheadLightPos, targetLightPos );
}
}
else
#endif
{
lightPos = Lerp( shadow.m_LightPosLerp, currLightPos, targetLightPos );
}


Assert( nActiveFlashlightCount<= 1 );  
if ( shadow.m_LightPosLerp >= 1.0f )
if ( nActiveFlashlightCount > 0 )
{
shadow.m_CurrentLightPos = shadow.m_TargetLightPos;
}
}
else if ( shadow.m_LightPosLerp >= 1.0f )
{
{
Assert( ( m_Shadows[ pActiveFlashlights[0] ].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) != 0 );
// Check if we have a new closest light position and start a new lerp
shadowmgr->SetFlashlightRenderState( pActiveFlashlights[0] );
float flDistSq = ( lightPos - shadow.m_CurrentLightPos ).LengthSqr();
 
if ( flDistSq > 1.0f )
{
// Light position has changed, which means we got a new light source. Initiate a lerp
shadow.m_TargetLightPos = lightPos;
shadow.m_LightPosLerp = 0.0f;
}
 
lightPos = shadow.m_CurrentLightPos;
}
 
if ( lightPos.x == FLT_MAX )
{
lightPos = origin - 200.0f * GetShadowDirection();
}
}
else
 
Vector vecResult( origin - lightPos );
vecResult.NormalizeInPlace();
 
vecResult.z *= r_worldlight_shortenfactor.GetFloat();
vecResult.NormalizeInPlace();
 
shadow.m_ShadowDir = vecResult;
 
if ( r_worldlight_debug.GetBool() )
{
{
shadowmgr->SetFlashlightRenderState( SHADOW_HANDLE_INVALID );
NDebugOverlay::Line( lightPos, origin, 255, 255, 0, false, 0.0f );
}
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Re-render shadow depth textures that lie in the leaf list
// Purpose:
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeShadowDepthTextures( const CViewSetup &viewSetup )
void CClientShadowMgr::UpdateDirtyShadow( ClientShadowHandle_t handle )
{
{
VPROF_BUDGET( "CClientShadowMgr::ComputeShadowDepthTextures", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
Assert( m_Shadows.IsValidIndex( handle ) );
if ( IsShadowingFromWorldLights() )
UpdateShadowDirectionFromLocalLightSource( handle );
UpdateProjectedTextureInternal( handle, false );
}


CMatRenderContextPtr pRenderContext( materials );
//-----------------------------------------------------------------------------
PIXEVENT( pRenderContext, "Shadow Depth Textures" );
// Purpose:
//-----------------------------------------------------------------------------
void WorldLightCastShadowCallback( IConVar *pVar, const char *pszOldValue, float flOldValue )
{
s_ClientShadowMgr.SetShadowFromWorldLightsEnabled( r_worldlight_castshadows.GetBool() );
}


// Build list of active render-to-texture shadows
//-----------------------------------------------------------------------------
ClientShadowHandle_t pActiveDepthShadows[1024];
// Purpose:
int nActiveDepthShadowCount = BuildActiveShadowDepthList( viewSetup, ARRAYSIZE( pActiveDepthShadows ), pActiveDepthShadows );
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetShadowFromWorldLightsEnabled( bool bEnabled )
{
if ( bEnabled == IsShadowingFromWorldLights() )
return;
m_bShadowFromWorldLights = bEnabled;
UpdateAllShadows();
}


// Iterate over all existing textures and allocate shadow textures
//-----------------------------------------------------------------------------
bool bDebugFrustum = r_flashlightdrawfrustum.GetBool();
// A material proxy that resets the base texture to use the rendered shadow
for ( int j = 0; j < nActiveDepthShadowCount; ++j )
//-----------------------------------------------------------------------------
{
class CShadowProxy : public IMaterialProxy
ClientShadow_t& shadow = m_Shadows[ pActiveDepthShadows[j] ];
{
public:
CShadowProxy();
virtual ~CShadowProxy();
virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
virtual void OnBind( void *pProxyData );
virtual void Release( void ) { delete this; }
virtual IMaterial *GetMaterial();


CTextureReference shadowDepthTexture;
private:
bool bGotShadowDepthTexture = LockShadowDepthTexture( &shadowDepthTexture );
IMaterialVar* m_BaseTextureVar;
if ( !bGotShadowDepthTexture )
};
{
// If we don't get one, that means we have too many this frame so bind no depth texture
static int bitchCount = 0;
if( bitchCount < 10 )
{
Warning( "Too many shadow maps this frame!\n"  );
bitchCount++;
}


Assert(0);
CShadowProxy::CShadowProxy()
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
{
continue;
m_BaseTextureVar = NULL;
}
}


CViewSetup shadowView;
CShadowProxy::~CShadowProxy()
shadowView.m_flAspectRatio = 1.0f;
{
shadowView.x = shadowView.y = 0;
}
shadowView.width = shadowDepthTexture->GetActualWidth();
shadowView.height = shadowDepthTexture->GetActualHeight();
shadowView.m_bOrtho = false;
shadowView.m_bDoBloomAndToneMapping = false;


// Copy flashlight parameters
bool CShadowProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
const FlashlightState_t& flashlightState = shadowmgr->GetFlashlightState( shadow.m_ShadowHandle );
{
shadowView.fov = shadowView.fovViewmodel = flashlightState.m_fHorizontalFOVDegrees;
bool foundVar;
shadowView.origin = flashlightState.m_vecLightOrigin;
m_BaseTextureVar = pMaterial->FindVar( "$basetexture", &foundVar, false );
QuaternionAngles( flashlightState.m_quatOrientation, shadowView.angles ); // Convert from Quaternion to QAngle
return foundVar;
}


shadowView.zNear = shadowView.zNearViewmodel = flashlightState.m_NearZ;
void CShadowProxy::OnBind( void *pProxyData )
shadowView.zFar = shadowView.zFarViewmodel = flashlightState.m_FarZ;
{
 
unsigned short clientShadowHandle = ( unsigned short )(int)pProxyData&0xffff;
// Can turn on all light frustum overlays or per light with flashlightState parameter...
ITexture* pTex = s_ClientShadowMgr.GetShadowTexture( clientShadowHandle );
if ( bDebugFrustum || flashlightState.m_bDrawShadowFrustum )
m_BaseTextureVar->SetTextureValue( pTex );
{
if ( ToolsEnabled() )
DebugDrawFrustum( shadowView.origin, shadow.m_WorldToShadow );
{
}
ToolFramework_RecordMaterialParams( GetMaterial() );
 
// Set depth bias factors specific to this flashlight
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->SetShadowDepthBiasFactors( flashlightState.m_flShadowSlopeScaleDepthBias, flashlightState.m_flShadowDepthBias );
 
// Render to the shadow depth texture with appropriate view
view->UpdateShadowDepthTexture( m_DummyColorTexture, shadowDepthTexture, shadowView );
 
// Associate the shadow depth texture and stencil bit with the flashlight for use during scene rendering
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, shadowDepthTexture, 0 );
}
}
SetViewFlashlightState( nActiveDepthShadowCount, pActiveDepthShadows );
}
}


IMaterial *CShadowProxy::GetMaterial()
//-----------------------------------------------------------------------------
// Re-renders all shadow textures for shadow casters that lie in the leaf list
//-----------------------------------------------------------------------------
static void SetupBonesOnBaseAnimating( C_BaseAnimating *&pBaseAnimating )
{
{
pBaseAnimating->SetupBones( NULL, -1, -1, gpGlobals->curtime );
return m_BaseTextureVar->GetOwningMaterial();
}
}


EXPOSE_INTERFACE( CShadowProxy, IMaterialProxy, "Shadow" IMATERIAL_PROXY_INTERFACE_VERSION );


void CClientShadowMgr::ComputeShadowTextures( const CViewSetup &view, int leafCount, LeafIndex_t* pLeafList )
//-----------------------------------------------------------------------------
// A material proxy that resets the base texture to use the rendered shadow
//-----------------------------------------------------------------------------
class CShadowModelProxy : public IMaterialProxy
{
{
VPROF_BUDGET( "CClientShadowMgr::ComputeShadowTextures", VPROF_BUDGETGROUP_SHADOW_RENDERING );
public:
CShadowModelProxy();
virtual ~CShadowModelProxy();
virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
virtual void OnBind( void *pProxyData );
virtual void Release( void ) { delete this; }
virtual IMaterial *GetMaterial();


if ( !m_RenderToTextureActive || (r_shadows.GetInt() == 0) || r_shadows_gamecontrol.GetInt() == 0 )
private:
return;
IMaterialVar* m_BaseTextureVar;
IMaterialVar* m_BaseTextureOffsetVar;
IMaterialVar* m_BaseTextureScaleVar;
IMaterialVar* m_BaseTextureMatrixVar;
IMaterialVar* m_FalloffOffsetVar;
IMaterialVar* m_FalloffDistanceVar;
IMaterialVar* m_FalloffAmountVar;
};


m_bThreaded = false;//( r_threaded_client_shadow_manager.GetBool() && g_pThreadPool->NumIdleThreads() );
CShadowModelProxy::CShadowModelProxy()
{
m_BaseTextureVar = NULL;
m_BaseTextureOffsetVar = NULL;
m_BaseTextureScaleVar = NULL;
m_BaseTextureMatrixVar = NULL;
m_FalloffOffsetVar = NULL;
m_FalloffDistanceVar = NULL;
m_FalloffAmountVar = NULL;
}


MDLCACHE_CRITICAL_SECTION();
CShadowModelProxy::~CShadowModelProxy()
// First grab all shadow textures we may want to render
{
int nCount = s_VisibleShadowList.FindShadows( &view, leafCount, pLeafList );
}
if ( nCount == 0 )
return;


// FIXME: Add heuristics based on distance, etc. to futz with
bool CShadowModelProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
// the shadow allocator + to select blobby shadows
{
 
bool foundVar;
CMatRenderContextPtr pRenderContext( materials );
m_BaseTextureVar = pMaterial->FindVar( "$basetexture", &foundVar, false );
 
if (!foundVar)
PIXEVENT( pRenderContext, "Render-To-Texture Shadows" );
return false;
 
m_BaseTextureOffsetVar = pMaterial->FindVar( "$basetextureoffset", &foundVar, false );
// Clear to white (color unused), black alpha
if (!foundVar)
pRenderContext->ClearColor4ub( 255, 255, 255, 0 );
return false;
m_BaseTextureScaleVar = pMaterial->FindVar( "$basetexturescale", &foundVar, false );
if (!foundVar)
return false;
m_BaseTextureMatrixVar = pMaterial->FindVar( "$basetexturetransform", &foundVar, false );
if (!foundVar)
return false;
m_FalloffOffsetVar = pMaterial->FindVar( "$falloffoffset", &foundVar, false );
if (!foundVar)
return false;
m_FalloffDistanceVar = pMaterial->FindVar( "$falloffdistance", &foundVar, false );
if (!foundVar)
return false;
m_FalloffAmountVar = pMaterial->FindVar( "$falloffamount", &foundVar, false );
return foundVar;
}


// No height clip mode please.
void CShadowModelProxy::OnBind( void *pProxyData )
MaterialHeightClipMode_t oldHeightClipMode = pRenderContext->GetHeightClipMode();
{
pRenderContext->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE );
unsigned short clientShadowHandle = ( unsigned short )((int)pProxyData&0xffff);
ITexture* pTex = s_ClientShadowMgr.GetShadowTexture( clientShadowHandle );
m_BaseTextureVar->SetTextureValue( pTex );


// No projection matrix (orthographic mode)
const ShadowInfo_t& info = s_ClientShadowMgr.GetShadowInfo( clientShadowHandle );
// FIXME: Make it work for projective shadows?
m_BaseTextureMatrixVar->SetMatrixValue( info.m_WorldToShadow );
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
m_BaseTextureOffsetVar->SetVecValue( info.m_TexOrigin.Base(), 2 );
pRenderContext->PushMatrix();
m_BaseTextureScaleVar->SetVecValue( info.m_TexSize.Base(), 2 );
pRenderContext->LoadIdentity();
m_FalloffOffsetVar->SetFloatValue( info.m_FalloffOffset );
pRenderContext->Scale( 1, -1, 1 );
m_FalloffDistanceVar->SetFloatValue( info.m_MaxDist );
pRenderContext->Ortho( 0, 0, 1, 1, -9999, 0 );
m_FalloffAmountVar->SetFloatValue( info.m_FalloffAmount );


pRenderContext->MatrixMode( MATERIAL_VIEW );
if ( ToolsEnabled() )
pRenderContext->PushMatrix();
{
 
pRenderContext->PushRenderTargetAndViewport( m_ShadowAllocator.GetTexture() );
 
if ( !IsX360() && m_bRenderTargetNeedsClear )
{
// don't need to clear absent depth buffer
pRenderContext->ClearBuffers( true, false );
m_bRenderTargetNeedsClear = false;
}
 
int nMaxShadows = r_shadowmaxrendered.GetInt();
int nModelsRendered = 0;
int i;
 
if ( m_bThreaded && g_pThreadPool->NumIdleThreads() )
{
s_NPCShadowBoneSetups.RemoveAll();
s_NonNPCShadowBoneSetups.RemoveAll();
 
for (i = 0; i < nCount; ++i)
{
const VisibleShadowInfo_t &info = s_VisibleShadowList.GetVisibleShadow(i);
if ( nModelsRendered < nMaxShadows )
{
if ( BuildSetupListForRenderToTextureShadow( info.m_hShadow, info.m_flArea ) )
{
++nModelsRendered;
}
}
}
 
ParallelProcess( "NPCShadowBoneSetups", s_NPCShadowBoneSetups.Base(), s_NPCShadowBoneSetups.Count(), &SetupBonesOnBaseAnimating );
ParallelProcess( "NonNPCShadowBoneSetups", s_NonNPCShadowBoneSetups.Base(), s_NonNPCShadowBoneSetups.Count(), &SetupBonesOnBaseAnimating );
 
nModelsRendered = 0;
}
 
for (i = 0; i < nCount; ++i)
{
const VisibleShadowInfo_t &info = s_VisibleShadowList.GetVisibleShadow(i);
if ( nModelsRendered < nMaxShadows )
{
if ( DrawRenderToTextureShadow( info.m_hShadow, info.m_flArea ) )
{
++nModelsRendered;
}
}
else
{
DrawRenderToTextureShadowLOD( info.m_hShadow );
}
}
 
// Render to the backbuffer again
pRenderContext->PopRenderTargetAndViewport();
 
// Restore the matrices
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PopMatrix();
 
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PopMatrix();
 
pRenderContext->SetHeightClipMode( oldHeightClipMode );
 
pRenderContext->SetHeightClipMode( oldHeightClipMode );
 
// Restore the clear color
pRenderContext->ClearColor3ub( 0, 0, 0 );
}
 
//-------------------------------------------------------------------------------------------------------
// Lock down the usage of a shadow depth texture...must be unlocked for use on subsequent views / frames
//-------------------------------------------------------------------------------------------------------
bool CClientShadowMgr::LockShadowDepthTexture( CTextureReference *shadowDepthTexture )
{
for ( int i=0; i < m_DepthTextureCache.Count(); i++ ) // Search for cached shadow depth texture
{
if ( m_DepthTextureCacheLocks[i] == false ) // If a free one is found
{
*shadowDepthTexture = m_DepthTextureCache[i];
m_DepthTextureCacheLocks[i] = true;
return true;
}
}
 
return false; // Didn't find it...
}
 
//------------------------------------------------------------------
// Unlock shadow depth texture for use on subsequent views / frames
//------------------------------------------------------------------
void CClientShadowMgr::UnlockAllShadowDepthTextures()
{
for (int i=0; i< m_DepthTextureCache.Count(); i++ )
{
m_DepthTextureCacheLocks[i] = false;
}
SetViewFlashlightState( 0, NULL );
}
 
void CClientShadowMgr::SetFlashlightTarget( ClientShadowHandle_t shadowHandle, EHANDLE targetEntity )
{
Assert( m_Shadows.IsValidIndex( shadowHandle ) );
 
CClientShadowMgr::ClientShadow_t &shadow = m_Shadows[ shadowHandle ];
if( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 )
return;
 
// shadow.m_pTargetRenderable = pRenderable;
shadow.m_hTargetEntity = targetEntity;
}
 
 
void CClientShadowMgr::SetFlashlightLightWorld( ClientShadowHandle_t shadowHandle, bool bLightWorld )
{
Assert( m_Shadows.IsValidIndex( shadowHandle ) );
 
ClientShadow_t &shadow = m_Shadows[ shadowHandle ];
if( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 )
return;
 
if ( bLightWorld )
{
shadow.m_Flags |= SHADOW_FLAGS_LIGHT_WORLD;
}
else
{
shadow.m_Flags &= ~SHADOW_FLAGS_LIGHT_WORLD;
}
}
 
 
bool CClientShadowMgr::IsFlashlightTarget( ClientShadowHandle_t shadowHandle, IClientRenderable *pRenderable )
{
ClientShadow_t &shadow = m_Shadows[ shadowHandle ];
 
if( shadow.m_hTargetEntity->GetClientRenderable() == pRenderable )
return true;
 
C_BaseEntity *pChild = shadow.m_hTargetEntity->FirstMoveChild();
while( pChild )
{
if( pChild->GetClientRenderable()==pRenderable )
return true;
 
pChild = pChild->NextMovePeer();
}
return false;
}
 
 
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const Vector &CClientShadowMgr::GetShadowDirection( ClientShadowHandle_t shadowHandle ) const
{
Assert( shadowHandle != CLIENTSHADOW_INVALID_HANDLE );
 
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[shadowHandle].m_Entity );
Assert( pRenderable );
 
if ( !IsShadowingFromWorldLights() )
{
return GetShadowDirection( pRenderable );
}
 
Vector &vecResult = AllocTempVector();
vecResult = m_Shadows[shadowHandle].m_ShadowDir;
 
// Allow the renderable to override the default
pRenderable->GetShadowCastDirection( &vecResult, GetActualShadowCastType( pRenderable ) );
 
return vecResult;
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateShadowDirectionFromLocalLightSource( ClientShadowHandle_t shadowHandle )
{
Assert( shadowHandle != CLIENTSHADOW_INVALID_HANDLE );
 
ClientShadow_t &shadow = m_Shadows[shadowHandle];
 
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
 
// TODO: Figure out why this still gets hit
Assert( pRenderable );
if ( !pRenderable )
{
DevWarning( "%s(): Skipping shadow with invalid client renderable (shadow handle %d)\n", __FUNCTION__, shadowHandle );
return;
}
 
Vector bbMin, bbMax;
pRenderable->GetRenderBoundsWorldspace( bbMin, bbMax );
Vector origin( 0.5f * ( bbMin + bbMax ) );
origin.z = bbMin.z; // Putting origin at the bottom of the bounding box makes the shadows a little shorter
Vector lightPos;
Vector lightBrightness;
 
if ( shadow.m_LightPosLerp >= 1.0f ) // Skip finding new light source if we're in the middle of a lerp
{
// Calculate minimum brightness squared
float flMinBrightnessSqr = r_worldlight_mincastintensity.GetFloat();
flMinBrightnessSqr *= flMinBrightnessSqr;
 
if ( g_pWorldLights->GetBrightestLightSource( pRenderable->GetRenderOrigin(), lightPos, lightBrightness ) == false || lightBrightness.LengthSqr() < flMinBrightnessSqr )
{
// Didn't find a light source at all, use default shadow direction
// TODO: Could switch to using blobby shadow in this case
lightPos.Init( FLT_MAX, FLT_MAX, FLT_MAX );
}
}
 
if ( shadow.m_LightPosLerp == FLT_MAX ) // First light pos ever, just init
{
shadow.m_CurrentLightPos = lightPos;
shadow.m_TargetLightPos = lightPos;
shadow.m_LightPosLerp = 1.0f;
}
else if ( shadow.m_LightPosLerp < 1.0f )
{
// We're in the middle of a lerp from current to target light. Finish it.
shadow.m_LightPosLerp += gpGlobals->frametime * 1.0f / r_worldlight_lerptime.GetFloat();
shadow.m_LightPosLerp = clamp( shadow.m_LightPosLerp, 0.0f, 1.0f );
 
Vector currLightPos( shadow.m_CurrentLightPos );
Vector targetLightPos( shadow.m_TargetLightPos );
if ( currLightPos.x == FLT_MAX )
{
currLightPos = origin - 200.0f * GetShadowDirection();
}
if ( targetLightPos.x == FLT_MAX )
{
targetLightPos = origin - 200.0f * GetShadowDirection();
}
 
// Lerp light pos
Vector v1 = origin - shadow.m_CurrentLightPos;
v1.NormalizeInPlace();
 
Vector v2 = origin - shadow.m_TargetLightPos;
v2.NormalizeInPlace();
 
// SAULUNDONE: Caused over top sweeping far too often
#if 0
if ( v1.Dot( v2 ) < 0.0f )
{
// If change in shadow angle is more than 90 degrees, lerp over the renderable's top to avoid long sweeping shadows
Vector fakeOverheadLightPos( origin.x, origin.y, origin.z + 200.0f );
if ( shadow.m_LightPosLerp < 0.5f )
{
lightPos = Lerp( 2.0f * shadow.m_LightPosLerp, currLightPos, fakeOverheadLightPos );
}
else
{
lightPos = Lerp( 2.0f * shadow.m_LightPosLerp - 1.0f, fakeOverheadLightPos, targetLightPos );
}
}
else
#endif
{
lightPos = Lerp( shadow.m_LightPosLerp, currLightPos, targetLightPos );
}
 
if ( shadow.m_LightPosLerp >= 1.0f )
{
shadow.m_CurrentLightPos = shadow.m_TargetLightPos;
}
}
else if ( shadow.m_LightPosLerp >= 1.0f )
{
// Check if we have a new closest light position and start a new lerp
float flDistSq = ( lightPos - shadow.m_CurrentLightPos ).LengthSqr();
 
if ( flDistSq > 1.0f )
{
// Light position has changed, which means we got a new light source. Initiate a lerp
shadow.m_TargetLightPos = lightPos;
shadow.m_LightPosLerp = 0.0f;
}
 
lightPos = shadow.m_CurrentLightPos;
}
 
if ( lightPos.x == FLT_MAX )
{
lightPos = origin - 200.0f * GetShadowDirection();
}
 
Vector vecResult( origin - lightPos );
vecResult.NormalizeInPlace();
 
vecResult.z *= r_worldlight_shortenfactor.GetFloat();
vecResult.NormalizeInPlace();
 
shadow.m_ShadowDir = vecResult;
 
if ( r_worldlight_debug.GetBool() )
{
NDebugOverlay::Line( lightPos, origin, 255, 255, 0, false, 0.0f );
}
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateDirtyShadow( ClientShadowHandle_t handle )
{
Assert( m_Shadows.IsValidIndex( handle ) );
if ( IsShadowingFromWorldLights() )
UpdateShadowDirectionFromLocalLightSource( handle );
UpdateProjectedTextureInternal( handle, false );
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void WorldLightCastShadowCallback( IConVar *pVar, const char *pszOldValue, float flOldValue )
{
s_ClientShadowMgr.SetShadowFromWorldLightsEnabled( r_worldlight_castshadows.GetBool() );
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetShadowFromWorldLightsEnabled( bool bEnabled )
{
if ( bEnabled == IsShadowingFromWorldLights() )
return;
m_bShadowFromWorldLights = bEnabled;
UpdateAllShadows();
}
 
//-----------------------------------------------------------------------------
// A material proxy that resets the base texture to use the rendered shadow
//-----------------------------------------------------------------------------
class CShadowProxy : public IMaterialProxy
{
public:
CShadowProxy();
virtual ~CShadowProxy();
virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
virtual void OnBind( void *pProxyData );
virtual void Release( void ) { delete this; }
virtual IMaterial *GetMaterial();
 
private:
IMaterialVar* m_BaseTextureVar;
};
 
CShadowProxy::CShadowProxy()
{
m_BaseTextureVar = NULL;
}
 
CShadowProxy::~CShadowProxy()
{
}
 
 
bool CShadowProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
bool foundVar;
m_BaseTextureVar = pMaterial->FindVar( "$basetexture", &foundVar, false );
return foundVar;
}
 
void CShadowProxy::OnBind( void *pProxyData )
{
unsigned short clientShadowHandle = ( unsigned short )(int)pProxyData&0xffff;
ITexture* pTex = s_ClientShadowMgr.GetShadowTexture( clientShadowHandle );
m_BaseTextureVar->SetTextureValue( pTex );
if ( ToolsEnabled() )
{
ToolFramework_RecordMaterialParams( GetMaterial() );
}
}
 
IMaterial *CShadowProxy::GetMaterial()
{
return m_BaseTextureVar->GetOwningMaterial();
}
 
EXPOSE_INTERFACE( CShadowProxy, IMaterialProxy, "Shadow" IMATERIAL_PROXY_INTERFACE_VERSION );
 
 
 
//-----------------------------------------------------------------------------
// A material proxy that resets the base texture to use the rendered shadow
//-----------------------------------------------------------------------------
class CShadowModelProxy : public IMaterialProxy
{
public:
CShadowModelProxy();
virtual ~CShadowModelProxy();
virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
virtual void OnBind( void *pProxyData );
virtual void Release( void ) { delete this; }
virtual IMaterial *GetMaterial();
 
private:
IMaterialVar* m_BaseTextureVar;
IMaterialVar* m_BaseTextureOffsetVar;
IMaterialVar* m_BaseTextureScaleVar;
IMaterialVar* m_BaseTextureMatrixVar;
IMaterialVar* m_FalloffOffsetVar;
IMaterialVar* m_FalloffDistanceVar;
IMaterialVar* m_FalloffAmountVar;
};
 
CShadowModelProxy::CShadowModelProxy()
{
m_BaseTextureVar = NULL;
m_BaseTextureOffsetVar = NULL;
m_BaseTextureScaleVar = NULL;
m_BaseTextureMatrixVar = NULL;
m_FalloffOffsetVar = NULL;
m_FalloffDistanceVar = NULL;
m_FalloffAmountVar = NULL;
}
 
CShadowModelProxy::~CShadowModelProxy()
{
}
 
 
bool CShadowModelProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
bool foundVar;
m_BaseTextureVar = pMaterial->FindVar( "$basetexture", &foundVar, false );
if (!foundVar)
return false;
m_BaseTextureOffsetVar = pMaterial->FindVar( "$basetextureoffset", &foundVar, false );
if (!foundVar)
return false;
m_BaseTextureScaleVar = pMaterial->FindVar( "$basetexturescale", &foundVar, false );
if (!foundVar)
return false;
m_BaseTextureMatrixVar = pMaterial->FindVar( "$basetexturetransform", &foundVar, false );
if (!foundVar)
return false;
m_FalloffOffsetVar = pMaterial->FindVar( "$falloffoffset", &foundVar, false );
if (!foundVar)
return false;
m_FalloffDistanceVar = pMaterial->FindVar( "$falloffdistance", &foundVar, false );
if (!foundVar)
return false;
m_FalloffAmountVar = pMaterial->FindVar( "$falloffamount", &foundVar, false );
return foundVar;
}
 
void CShadowModelProxy::OnBind( void *pProxyData )
{
unsigned short clientShadowHandle = ( unsigned short )((int)pProxyData&0xffff);
ITexture* pTex = s_ClientShadowMgr.GetShadowTexture( clientShadowHandle );
m_BaseTextureVar->SetTextureValue( pTex );
 
const ShadowInfo_t& info = s_ClientShadowMgr.GetShadowInfo( clientShadowHandle );
m_BaseTextureMatrixVar->SetMatrixValue( info.m_WorldToShadow );
m_BaseTextureOffsetVar->SetVecValue( info.m_TexOrigin.Base(), 2 );
m_BaseTextureScaleVar->SetVecValue( info.m_TexSize.Base(), 2 );
m_FalloffOffsetVar->SetFloatValue( info.m_FalloffOffset );
m_FalloffDistanceVar->SetFloatValue( info.m_MaxDist );
m_FalloffAmountVar->SetFloatValue( info.m_FalloffAmount );
 
if ( ToolsEnabled() )
{
ToolFramework_RecordMaterialParams( GetMaterial() );
ToolFramework_RecordMaterialParams( GetMaterial() );
}
}
Line 4,825: Line 4,459:
EXPOSE_INTERFACE( CShadowModelProxy, IMaterialProxy, "ShadowModel" IMATERIAL_PROXY_INTERFACE_VERSION );
EXPOSE_INTERFACE( CShadowModelProxy, IMaterialProxy, "ShadowModel" IMATERIAL_PROXY_INTERFACE_VERSION );
</source>
</source>
{{Uncategorized|date=January 2024}}

Latest revision as of 10:02, 21 January 2024

Dead End - Icon.png
This article has no Wikipedia icon links to other VDC articles. Please help improve this article by adding links Wikipedia icon that are relevant to the context within the existing text.
January 2024
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
// Interface to the client system responsible for dealing with shadows
//
// Boy is this complicated. OK, lets talk about how this works at the moment
//
// The ClientShadowMgr contains all of the highest-level state for rendering
// shadows, and it controls the ShadowMgr in the engine which is the central
// clearing house for rendering shadows.
//
// There are two important types of objects with respect to shadows:
// the shadow receiver, and the shadow caster. How is the association made
// between casters + the receivers? Turns out it's done slightly differently 
// depending on whether the receiver is the world, or if it's an entity.
//
// In the case of the world, every time the engine's ProjectShadow() is called, 
// any previous receiver state stored (namely, which world surfaces are
// receiving shadows) are cleared. Then, when ProjectShadow is called, 
// the engine iterates over all nodes + leaves within the shadow volume and 
// marks front-facing surfaces in them as potentially being affected by the 
// shadow. Later on, if those surfaces are actually rendered, the surfaces
// are clipped by the shadow volume + rendered.
// 
// In the case of entities, there are slightly different methods depending
// on whether the receiver is a brush model or a studio model. However, there
// are a couple central things that occur with both.
//
// Every time a shadow caster is moved, the ClientLeafSystem's ProjectShadow
// method is called to tell it to remove the shadow from all leaves + all 
// renderables it's currently associated with. Then it marks each leaf in the
// shadow volume as being affected by that shadow, and it marks every renderable
// in that volume as being potentially affected by the shadow (the function
// AddShadowToRenderable is called for each renderable in leaves affected
// by the shadow volume).
//
// Every time a shadow receiver is moved, the ClientLeafSystem first calls 
// RemoveAllShadowsFromRenderable to have it clear out its state, and then
// the ClientLeafSystem calls AddShadowToRenderable() for all shadows in all
// leaves the renderable has moved into.
//
// Now comes the difference between brush models + studio models. In the case
// of brush models, when a shadow is added to the studio model, it's done in
// the exact same way as for the world. Surfaces on the brush model are marked
// as potentially being affected by the shadow, and if those surfaces are
// rendered, the surfaces are clipped to the shadow volume. When ProjectShadow()
// is called, turns out the same operation that removes the shadow that moved
// from the world surfaces also works to remove the shadow from brush surfaces.
//
// In the case of studio models, we need a separate operation to remove
// the shadow from all studio models
//===========================================================================//

#include "cbase.h"
#include "engine/ishadowmgr.h"
#include "model_types.h"
#include "bitmap/imageformat.h"
#include "materialsystem/imaterialproxy.h"
#include "materialsystem/imaterialvar.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imesh.h"
#include "materialsystem/itexture.h"
#include "bsptreedata.h"
#include "utlmultilist.h"
#include "collisionutils.h"
#include "iviewrender.h"
#include "ivrenderview.h"
#include "tier0/vprof.h"
#include "engine/ivmodelinfo.h"
#include "view_shared.h"
#include "engine/ivdebugoverlay.h"
#include "engine/IStaticPropMgr.h"
#include "datacache/imdlcache.h"
#include "viewrender.h"
#include "tier0/icommandline.h"
#include "vstdlib/jobthread.h"
#include "toolframework_client.h"
#include "bonetoworldarray.h"
#include "cmodel.h"
#include "debugoverlay_shared.h"
#include "worldlight.h"
#include "bspfile.h"
#include "client_factorylist.h" // FactoryList_Retrieve
#include "eiface.h" // IVEngineServer
#include "filesystem.h"

static IVEngineServer *g_pEngineServer = nullptr;
static CWorldLights s_WorldLights;
CWorldLights *g_pWorldLights = &s_WorldLights;

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

static ConVar r_flashlightdrawfrustum( "r_flashlightdrawfrustum", "0" );
static ConVar r_flashlightmodels( "r_flashlightmodels", "1" );
static ConVar r_shadowrendertotexture( "r_shadowrendertotexture", "0" );
static ConVar r_flashlight_version2( "r_flashlight_version2", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
void WorldLightCastShadowCallback( IConVar *pVar, const char *pszOldValue, float flOldValue );
static ConVar r_worldlight_castshadows( "r_worldlight_castshadows", "1", FCVAR_CHEAT, "Allow world lights to cast shadows", true, 0, true, 1, WorldLightCastShadowCallback );
static ConVar r_worldlight_lerptime( "r_worldlight_lerptime", "0.5", FCVAR_CHEAT );
static ConVar r_worldlight_debug( "r_worldlight_debug", "0", FCVAR_CHEAT );
static ConVar r_worldlight_shortenfactor( "r_worldlight_shortenfactor", "2" , FCVAR_CHEAT, "Makes shadows cast from local lights shorter" );
static ConVar r_worldlight_mincastintensity( "r_worldlight_mincastintensity", "0.3", FCVAR_CHEAT, "Minimum brightness of a light to be classed as shadow casting", true, 0, false, 0 );

ConVar r_flashlightdepthtexture( "r_flashlightdepthtexture", "1" );

#if defined( _X360 )
ConVar r_flashlightdepthres( "r_flashlightdepthres", "512" );
#else
ConVar r_flashlightdepthres( "r_flashlightdepthres", "1024" );
#endif

ConVar r_threaded_client_shadow_manager( "r_threaded_client_shadow_manager", "0" );

#ifdef _WIN32
#pragma warning( disable: 4701 )
#endif

// forward declarations
void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );

//-----------------------------------------------------------------------------
// A texture allocator used to batch textures together
// At the moment, the implementation simply allocates blocks of max 256x256
// and each block stores an array of uniformly-sized textures
//-----------------------------------------------------------------------------
typedef unsigned short TextureHandle_t;
enum
{
	INVALID_TEXTURE_HANDLE = (TextureHandle_t)~0
};

class CTextureAllocator
{
public:
	// Initialize the allocator with something that knows how to refresh the bits
	void			Init();
	void			Shutdown();

	// Resets the allocator
	void			Reset();

	// Deallocates everything
	void			DeallocateAllTextures();

	// Allocate, deallocate texture
	TextureHandle_t	AllocateTexture( int w, int h );
	void			DeallocateTexture( TextureHandle_t h );

	// Mark texture as being used... (return true if re-render is needed)
	bool			UseTexture( TextureHandle_t h, bool bWillRedraw, float flArea );
	bool			HasValidTexture( TextureHandle_t h );

	// Advance frame...
	void			AdvanceFrame();

	// Get at the location of the texture
	void			GetTextureRect(TextureHandle_t handle, int& x, int& y, int& w, int& h );

	// Get at the texture it's a part of
	ITexture		*GetTexture();
	
	// Get at the total texture size.
	void			GetTotalTextureSize( int& w, int& h );

	void			DebugPrintCache( void );

private:
	typedef unsigned short FragmentHandle_t;

	enum
	{
		INVALID_FRAGMENT_HANDLE = (FragmentHandle_t)~0,
		TEXTURE_PAGE_SIZE	    = 1024,
		MAX_TEXTURE_POWER    	= 8,
#if !defined( _X360 )
		MIN_TEXTURE_POWER	    = 4,
#else
		MIN_TEXTURE_POWER	    = 5,	// per resolve requirements to ensure 32x32 aligned offsets
#endif
		MAX_TEXTURE_SIZE	    = (1 << MAX_TEXTURE_POWER),
		MIN_TEXTURE_SIZE	    = (1 << MIN_TEXTURE_POWER),
		BLOCK_SIZE			    = MAX_TEXTURE_SIZE,
		BLOCKS_PER_ROW		    = (TEXTURE_PAGE_SIZE / MAX_TEXTURE_SIZE),
		BLOCK_COUNT			    = (BLOCKS_PER_ROW * BLOCKS_PER_ROW),
	};

	struct TextureInfo_t
	{
		FragmentHandle_t	m_Fragment;
		unsigned short		m_Size;
		unsigned short		m_Power;
	};

	struct FragmentInfo_t
	{
		unsigned short	m_Block;
		unsigned short	m_Index;
		TextureHandle_t	m_Texture;

		// Makes sure we don't overflow
		unsigned int	m_FrameUsed;
	};

	struct BlockInfo_t
	{
		unsigned short	m_FragmentPower;
	};

	struct Cache_t
	{
		unsigned short	m_List;
	};

	// Adds a block worth of fragments to the LRU
	void AddBlockToLRU( int block );

	// Unlink fragment from cache
	void UnlinkFragmentFromCache( Cache_t& cache, FragmentHandle_t fragment );

	// Mark something as being used (MRU)..
	void MarkUsed( FragmentHandle_t fragment );

	// Mark something as being unused (LRU)..
	void MarkUnused( FragmentHandle_t fragment );

	// Disconnect texture from fragment
	void DisconnectTextureFromFragment( FragmentHandle_t f );

	// Returns the size of a particular fragment
	int	GetFragmentPower( FragmentHandle_t f ) const;

	// Stores the actual texture we're writing into
	CTextureReference	m_TexturePage;

	CUtlLinkedList< TextureInfo_t, TextureHandle_t >	m_Textures;
	CUtlMultiList< FragmentInfo_t, FragmentHandle_t >	m_Fragments;

	Cache_t		m_Cache[MAX_TEXTURE_POWER+1]; 
	BlockInfo_t	m_Blocks[BLOCK_COUNT];
	unsigned int m_CurrentFrame;
};

//-----------------------------------------------------------------------------
// Allocate/deallocate the texture page
//-----------------------------------------------------------------------------
void CTextureAllocator::Init()
{
	for ( int i = 0; i <= MAX_TEXTURE_POWER; ++i )
	{
		m_Cache[i].m_List = m_Fragments.InvalidIndex();
	}

#if !defined( _X360 )
	// don't need depth buffer for shadows
	m_TexturePage.InitRenderTarget( TEXTURE_PAGE_SIZE, TEXTURE_PAGE_SIZE, RT_SIZE_NO_CHANGE, IMAGE_FORMAT_ARGB8888, MATERIAL_RT_DEPTH_NONE, false, "_rt_Shadows" );
#else
	// unfortunate explicit management required for this render target
	// 32bpp edram is only largest shadow fragment, but resolved to actual shadow atlas
	// because full-res 1024x1024 shadow buffer is too large for EDRAM
	m_TexturePage.InitRenderTargetTexture( TEXTURE_PAGE_SIZE, TEXTURE_PAGE_SIZE, RT_SIZE_NO_CHANGE, IMAGE_FORMAT_ARGB8888, MATERIAL_RT_DEPTH_NONE, false, "_rt_Shadows" );

	// edram footprint is only 256x256x4 = 256K
	m_TexturePage.InitRenderTargetSurface( MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, IMAGE_FORMAT_ARGB8888, false );

	// due to texture/surface size mismatch, ensure texture page is entirely cleared translucent
	// otherwise border artifacts at edge of shadows due to pixel shader averaging of unwanted bits
	m_TexturePage->ClearTexture( 0, 0, 0, 0 );
#endif
}

void CTextureAllocator::Shutdown()
{
	m_TexturePage.Shutdown();
}

//-----------------------------------------------------------------------------
// Initialize the allocator with something that knows how to refresh the bits
//-----------------------------------------------------------------------------
void CTextureAllocator::Reset()
{
	DeallocateAllTextures();

	m_Textures.EnsureCapacity(256);
	m_Fragments.EnsureCapacity(256);

	// Set up the block sizes....
	// FIXME: Improve heuristic?!?
#if !defined( _X360 )
	m_Blocks[0].m_FragmentPower  = MAX_TEXTURE_POWER-4;	// 128 cells at ExE resolution
#else
	m_Blocks[0].m_FragmentPower  = MAX_TEXTURE_POWER-3;	// 64 cells at DxD resolution
#endif
	m_Blocks[1].m_FragmentPower  = MAX_TEXTURE_POWER-3;	// 64 cells at DxD resolution
	m_Blocks[2].m_FragmentPower  = MAX_TEXTURE_POWER-2;	// 32 cells at CxC resolution
	m_Blocks[3].m_FragmentPower  = MAX_TEXTURE_POWER-2;		 
	m_Blocks[4].m_FragmentPower  = MAX_TEXTURE_POWER-1;	// 24 cells at BxB resolution
	m_Blocks[5].m_FragmentPower  = MAX_TEXTURE_POWER-1;
	m_Blocks[6].m_FragmentPower  = MAX_TEXTURE_POWER-1;
	m_Blocks[7].m_FragmentPower  = MAX_TEXTURE_POWER-1;
	m_Blocks[8].m_FragmentPower  = MAX_TEXTURE_POWER-1;
	m_Blocks[9].m_FragmentPower  = MAX_TEXTURE_POWER-1;
	m_Blocks[10].m_FragmentPower = MAX_TEXTURE_POWER;	// 6 cells at AxA resolution
	m_Blocks[11].m_FragmentPower = MAX_TEXTURE_POWER;	 
	m_Blocks[12].m_FragmentPower = MAX_TEXTURE_POWER;
	m_Blocks[13].m_FragmentPower = MAX_TEXTURE_POWER;
	m_Blocks[14].m_FragmentPower = MAX_TEXTURE_POWER;
	m_Blocks[15].m_FragmentPower = MAX_TEXTURE_POWER;

	// Initialize the LRU
	int i;
	for ( i = 0; i <= MAX_TEXTURE_POWER; ++i )
	{
		m_Cache[i].m_List = m_Fragments.CreateList();
	}

	// Now that the block sizes are allocated, create LRUs for the various block sizes
	for ( i = 0; i < BLOCK_COUNT; ++i)
	{
		// Initialize LRU
		AddBlockToLRU( i );
	}

	m_CurrentFrame = 0;
}

void CTextureAllocator::DeallocateAllTextures()
{
	m_Textures.Purge();
	m_Fragments.Purge();
	for ( int i = 0; i <= MAX_TEXTURE_POWER; ++i )
	{
		m_Cache[i].m_List = m_Fragments.InvalidIndex();
	}
}

//-----------------------------------------------------------------------------
// Dump the state of the cache to debug out
//-----------------------------------------------------------------------------
void CTextureAllocator::DebugPrintCache( void )
{
	// For each fragment
	int nNumFragments = m_Fragments.TotalCount();
	int nNumInvalidFragments = 0;

	Warning("Fragments (%d):\n===============\n", nNumFragments);

	for ( int f = 0; f < nNumFragments; f++ )
	{
		if ( ( m_Fragments[f].m_FrameUsed != 0 ) && ( m_Fragments[f].m_Texture != INVALID_TEXTURE_HANDLE ) )
			Warning("Fragment %d, Block: %d, Index: %d, Texture: %d Frame Used: %d\n", f, m_Fragments[f].m_Block, m_Fragments[f].m_Index, m_Fragments[f].m_Texture, m_Fragments[f].m_FrameUsed );
		else
			nNumInvalidFragments++;
	}

	Warning("Invalid Fragments: %d\n", nNumInvalidFragments);

//	for ( int c = 0; c <= MAX_TEXTURE_POWER; ++c )
//	{
//		Warning("Cache Index (%d)\n", m_Cache[c].m_List);
//	}

}

//-----------------------------------------------------------------------------
// Adds a block worth of fragments to the LRU
//-----------------------------------------------------------------------------
void CTextureAllocator::AddBlockToLRU( int block )
{
	int power = m_Blocks[block].m_FragmentPower;
 	int size = (1 << power);

	// Compute the number of fragments in this block
	int fragmentCount = MAX_TEXTURE_SIZE / size;
	fragmentCount *= fragmentCount;

	// For each fragment, indicate which block it's a part of (and the index)
	// and then stick in at the top of the LRU
	while (--fragmentCount >= 0 )
	{
		FragmentHandle_t f = m_Fragments.Alloc( );
		m_Fragments[f].m_Block = block;
		m_Fragments[f].m_Index = fragmentCount;
		m_Fragments[f].m_Texture = INVALID_TEXTURE_HANDLE;
		m_Fragments[f].m_FrameUsed = 0xFFFFFFFF;
		m_Fragments.LinkToHead( m_Cache[power].m_List, f );
	}
}

//-----------------------------------------------------------------------------
// Unlink fragment from cache
//-----------------------------------------------------------------------------
void CTextureAllocator::UnlinkFragmentFromCache( Cache_t& cache, FragmentHandle_t fragment )
{
	m_Fragments.Unlink( cache.m_List, fragment);
}

//-----------------------------------------------------------------------------
// Mark something as being used (MRU)..
//-----------------------------------------------------------------------------
void CTextureAllocator::MarkUsed( FragmentHandle_t fragment )
{
	int block = m_Fragments[fragment].m_Block;
	int power = m_Blocks[block].m_FragmentPower;

	// Hook it at the end of the LRU
	Cache_t& cache = m_Cache[power];
	m_Fragments.LinkToTail( cache.m_List, fragment );
	m_Fragments[fragment].m_FrameUsed = m_CurrentFrame;
}

//-----------------------------------------------------------------------------
// Mark something as being unused (LRU)..
//-----------------------------------------------------------------------------
void CTextureAllocator::MarkUnused( FragmentHandle_t fragment )
{
	int block = m_Fragments[fragment].m_Block;
	int power = m_Blocks[block].m_FragmentPower;

	// Hook it at the end of the LRU
	Cache_t& cache = m_Cache[power];
	m_Fragments.LinkToHead( cache.m_List, fragment );
}

//-----------------------------------------------------------------------------
// Allocate, deallocate texture
//-----------------------------------------------------------------------------
TextureHandle_t	CTextureAllocator::AllocateTexture( int w, int h )
{
	// Implementational detail for now
	Assert( w == h );

	// Clamp texture size
	if (w < MIN_TEXTURE_SIZE)
		w = MIN_TEXTURE_SIZE;
	else if (w > MAX_TEXTURE_SIZE)
		w = MAX_TEXTURE_SIZE;

	TextureHandle_t handle = m_Textures.AddToTail();
	m_Textures[handle].m_Fragment = INVALID_FRAGMENT_HANDLE;
	m_Textures[handle].m_Size = w;

	// Find the power of two
	int power = 0;
	int size = 1;
	while(size < w)
	{
		size <<= 1;
		++power;
	}
	Assert( size == w );

	m_Textures[handle].m_Power = power;

	return handle;
}

void CTextureAllocator::DeallocateTexture( TextureHandle_t h )
{
//	Warning("Beginning of DeallocateTexture\n");
//	DebugPrintCache();

	if (m_Textures[h].m_Fragment != INVALID_FRAGMENT_HANDLE)
	{
		MarkUnused(m_Textures[h].m_Fragment);
		m_Fragments[m_Textures[h].m_Fragment].m_FrameUsed = 0xFFFFFFFF;	// non-zero frame
		DisconnectTextureFromFragment( m_Textures[h].m_Fragment );
	}
	m_Textures.Remove(h);

//	Warning("End of DeallocateTexture\n");
//	DebugPrintCache();
}

//-----------------------------------------------------------------------------
// Disconnect texture from fragment
//-----------------------------------------------------------------------------
void CTextureAllocator::DisconnectTextureFromFragment( FragmentHandle_t f )
{
//	Warning( "Beginning of DisconnectTextureFromFragment\n" );
//	DebugPrintCache();

	FragmentInfo_t& info = m_Fragments[f];
	if (info.m_Texture != INVALID_TEXTURE_HANDLE)
	{
		m_Textures[info.m_Texture].m_Fragment = INVALID_FRAGMENT_HANDLE;
		info.m_Texture = INVALID_TEXTURE_HANDLE;
	}

//	Warning( "End of DisconnectTextureFromFragment\n" );
//	DebugPrintCache();
}

//-----------------------------------------------------------------------------
// Do we have a valid texture assigned?
//-----------------------------------------------------------------------------
bool CTextureAllocator::HasValidTexture( TextureHandle_t h )
{
	TextureInfo_t& info = m_Textures[h];
	FragmentHandle_t currentFragment = info.m_Fragment;
	return (currentFragment != INVALID_FRAGMENT_HANDLE);
}

//-----------------------------------------------------------------------------
// Mark texture as being used...
//-----------------------------------------------------------------------------
bool CTextureAllocator::UseTexture( TextureHandle_t h, bool bWillRedraw, float flArea )
{
//	Warning( "Top of UseTexture\n" );
//	DebugPrintCache();

	TextureInfo_t& info = m_Textures[h];

	// spin up to the best fragment size
	int nDesiredPower = MIN_TEXTURE_POWER;
	int nDesiredWidth = MIN_TEXTURE_SIZE;
	while ( (nDesiredWidth * nDesiredWidth) < flArea )
	{
		if ( nDesiredPower >= info.m_Power )
		{
			nDesiredPower = info.m_Power;
			break;
		}

		++nDesiredPower;
		nDesiredWidth <<= 1;
	}

	// If we've got a valid fragment for this texture, no worries!
	int nCurrentPower = -1;
	FragmentHandle_t currentFragment = info.m_Fragment;
	if (currentFragment != INVALID_FRAGMENT_HANDLE)
	{
		// If the current fragment is at or near the desired power, we're done
		nCurrentPower = GetFragmentPower(info.m_Fragment);
		Assert( nCurrentPower <= info.m_Power );
		bool bShouldKeepTexture = (!bWillRedraw) && (nDesiredPower < 8) && (nDesiredPower - nCurrentPower <= 1);
		if ((nCurrentPower == nDesiredPower) || bShouldKeepTexture)
		{
			// Move to the back of the LRU
			MarkUsed( currentFragment );
			return false;
		}
	}

//	Warning( "\n\nUseTexture B\n" );
//	DebugPrintCache();

	// Grab the LRU fragment from the appropriate cache
	// If that fragment is connected to a texture, disconnect it.
	int power = nDesiredPower;

	FragmentHandle_t f = INVALID_FRAGMENT_HANDLE;
	bool done = false;
	while (!done && power >= 0)
	{
		f = m_Fragments.Head( m_Cache[power].m_List );
	
		// This represents an overflow condition (used too many textures of
		// the same size in a single frame). It that happens, just use a texture
		// of lower res.
		if ( (f != m_Fragments.InvalidIndex()) && (m_Fragments[f].m_FrameUsed != m_CurrentFrame) )
		{
			done = true;
		}
		else
		{
			--power;
		}
	}

//	Warning( "\n\nUseTexture C\n" );
//	DebugPrintCache();

	// Ok, lets see if we're better off than we were...
	if (currentFragment != INVALID_FRAGMENT_HANDLE)
	{
		if (power <= nCurrentPower)
		{
			// Oops... we're not. Let's leave well enough alone
			// Move to the back of the LRU
			MarkUsed( currentFragment );
			return false;
		}
		else
		{
			// Clear out the old fragment
			DisconnectTextureFromFragment(currentFragment);
		}
	}

	if ( f == INVALID_FRAGMENT_HANDLE )
	{
		return false;
	}

	// Disconnect existing texture from this fragment (if necessary)
	DisconnectTextureFromFragment(f);

	// Connnect new texture to this fragment
	info.m_Fragment = f;
	m_Fragments[f].m_Texture = h;

	// Move to the back of the LRU
	MarkUsed( f );

	// Indicate we need a redraw
	return true;
}

//-----------------------------------------------------------------------------
// Returns the size of a particular fragment
//-----------------------------------------------------------------------------
int	CTextureAllocator::GetFragmentPower( FragmentHandle_t f ) const
{
	return m_Blocks[m_Fragments[f].m_Block].m_FragmentPower;
}

//-----------------------------------------------------------------------------
// Advance frame...
//-----------------------------------------------------------------------------
void CTextureAllocator::AdvanceFrame()
{
	// Be sure that this is called as infrequently as possible (i.e. once per frame,
	// NOT once per view) to prevent cache thrash when rendering multiple views in a single frame
	m_CurrentFrame++;
}

//-----------------------------------------------------------------------------
// Prepare to render into texture...
//-----------------------------------------------------------------------------
ITexture* CTextureAllocator::GetTexture()
{
	return m_TexturePage;
}

//-----------------------------------------------------------------------------
// Get at the total texture size.
//-----------------------------------------------------------------------------
void CTextureAllocator::GetTotalTextureSize( int& w, int& h )
{
	w = h = TEXTURE_PAGE_SIZE;
}

//-----------------------------------------------------------------------------
// Returns the rectangle the texture lives in..
//-----------------------------------------------------------------------------
void CTextureAllocator::GetTextureRect(TextureHandle_t handle, int& x, int& y, int& w, int& h )
{
	TextureInfo_t& info = m_Textures[handle];
	Assert( info.m_Fragment != INVALID_FRAGMENT_HANDLE );

	// Compute the position of the fragment in the page
	FragmentInfo_t& fragment = m_Fragments[info.m_Fragment];
	int blockY = fragment.m_Block / BLOCKS_PER_ROW;
	int blockX = fragment.m_Block - blockY * BLOCKS_PER_ROW;

	int fragmentSize = (1 << m_Blocks[fragment.m_Block].m_FragmentPower);
	int fragmentsPerRow = BLOCK_SIZE / fragmentSize;
	int fragmentY = fragment.m_Index / fragmentsPerRow;
	int fragmentX = fragment.m_Index - fragmentY * fragmentsPerRow;

	x = blockX * BLOCK_SIZE + fragmentX * fragmentSize;
	y = blockY * BLOCK_SIZE + fragmentY * fragmentSize;
	w = fragmentSize;
	h = fragmentSize;
}

//-----------------------------------------------------------------------------
// Defines how big of a shadow texture we should be making per caster...
//-----------------------------------------------------------------------------
#define TEXEL_SIZE_PER_CASTER_SIZE	2.0f 
#define MAX_FALLOFF_AMOUNT 240
#define MAX_CLIP_PLANE_COUNT 4
#define SHADOW_CULL_TOLERANCE 0.5f

static ConVar r_shadows( "r_shadows", "1" ); // hook into engine's cvars..
static ConVar r_shadowmaxrendered("r_shadowmaxrendered", "32");
static ConVar r_shadows_gamecontrol( "r_shadows_gamecontrol", "-1", FCVAR_CHEAT );	 // hook into engine's cvars..

//-----------------------------------------------------------------------------
// The class responsible for dealing with shadows on the client side
// Oh, and let's take a moment and notice how happy Robin and John must be 
// owing to the lack of space between this lovely comment and the class name =)
//-----------------------------------------------------------------------------
class CClientShadowMgr : public IClientShadowMgr
{
public:
	CClientShadowMgr();

	virtual char const *Name() { return "CCLientShadowMgr"; }

	// Inherited from IClientShadowMgr
	virtual bool Init();
	virtual void PostInit() {}
	virtual void Shutdown();
	virtual void LevelInitPreEntity();
	virtual void LevelInitPostEntity() {}
	virtual void LevelShutdownPreEntity() {}
	virtual void LevelShutdownPostEntity();

	virtual bool IsPerFrame() { return true; }

	virtual void PreRender();
	virtual void Update( float frametime ) { }
	virtual void PostRender() {}

	virtual void OnSave() {}
	virtual void OnRestore() {}
	virtual void SafeRemoveIfDesired() {}

	virtual ClientShadowHandle_t CreateShadow( ClientEntityHandle_t entity, int flags );
	virtual void DestroyShadow( ClientShadowHandle_t handle );

	// Create flashlight (projected texture light source)
	virtual ClientShadowHandle_t CreateFlashlight( const FlashlightState_t &lightState );
	virtual void UpdateFlashlightState( ClientShadowHandle_t shadowHandle, const FlashlightState_t &lightState );
	virtual void DestroyFlashlight( ClientShadowHandle_t shadowHandle );

	// Update a shadow
	virtual void UpdateProjectedTexture( ClientShadowHandle_t handle, bool force );

	void ComputeBoundingSphere( IClientRenderable* pRenderable, Vector& origin, float& radius );

	virtual void AddToDirtyShadowList( ClientShadowHandle_t handle, bool bForce );
	virtual void AddToDirtyShadowList( IClientRenderable *pRenderable, bool force );

	// Marks the render-to-texture shadow as needing to be re-rendered
	virtual void MarkRenderToTextureShadowDirty( ClientShadowHandle_t handle );

	// deals with shadows being added to shadow receivers
	void AddShadowToReceiver( ClientShadowHandle_t handle,
		IClientRenderable* pRenderable, ShadowReceiver_t type );

	// deals with shadows being added to shadow receivers
	void RemoveAllShadowsFromReceiver( IClientRenderable* pRenderable, ShadowReceiver_t type );

	// Re-renders all shadow textures for shadow casters that lie in the leaf list
	void ComputeShadowTextures( const CViewSetup &view, int leafCount, LeafIndex_t* pLeafList );

	// Kicks off rendering into shadow depth maps (if any)
	void ComputeShadowDepthTextures( const CViewSetup &view );

	// Frees shadow depth textures for use in subsequent view/frame
	void FreeShadowDepthTextures();

	// Returns the shadow texture
	ITexture* GetShadowTexture( unsigned short h );

	// Returns shadow information
	const ShadowInfo_t& GetShadowInfo( ClientShadowHandle_t h );

	// Renders the shadow texture to screen...
	void RenderShadowTexture( int w, int h );

	// Sets the shadow direction
	virtual void SetShadowDirection( const Vector& dir );
	const Vector &GetShadowDirection() const;

	// Sets the shadow color
	virtual void SetShadowColor( unsigned char r, unsigned char g, unsigned char b );
	void GetShadowColor( unsigned char *r, unsigned char *g, unsigned char *b ) const;

	// Sets the shadow distance
	virtual void SetShadowDistance( float flMaxDistance );
	float GetShadowDistance( ) const;

	// Sets the screen area at which blobby shadows are always used
	virtual void SetShadowBlobbyCutoffArea( float flMinArea );
	float GetBlobbyCutoffArea( ) const;

	// Set the darkness falloff bias
	virtual void SetFalloffBias( ClientShadowHandle_t handle, unsigned char ucBias );

	void RestoreRenderState();

	// Computes a rough bounding box encompassing the volume of the shadow
    void ComputeShadowBBox( IClientRenderable *pRenderable, ClientShadowHandle_t shadowHandle, const Vector &vecAbsCenter, float flRadius, Vector *pAbsMins, Vector *pAbsMaxs );

	bool WillParentRenderBlobbyShadow( IClientRenderable *pRenderable );

	// Are we the child of a shadow with render-to-texture?
	bool ShouldUseParentShadow( IClientRenderable *pRenderable );

	void SetShadowsDisabled( bool bDisabled ) 
	{ 
		r_shadows_gamecontrol.SetValue( bDisabled != 1 );
	}

	void SuppressShadowFromWorldLights( bool bSuppress );
    void SetShadowFromWorldLightsEnabled( bool bEnabled );
    bool IsShadowingFromWorldLights() const { return m_bShadowFromWorldLights; }

private:
	enum
	{
		SHADOW_FLAGS_TEXTURE_DIRTY =	(CLIENT_SHADOW_FLAGS_LAST_FLAG << 1),
		SHADOW_FLAGS_BRUSH_MODEL =		(CLIENT_SHADOW_FLAGS_LAST_FLAG << 2), 
		SHADOW_FLAGS_USING_LOD_SHADOW = (CLIENT_SHADOW_FLAGS_LAST_FLAG << 3),
		SHADOW_FLAGS_LIGHT_WORLD =		(CLIENT_SHADOW_FLAGS_LAST_FLAG << 4),
	};

	struct ClientShadow_t
	{
		ClientEntityHandle_t	m_Entity;
		ShadowHandle_t			m_ShadowHandle;
		ClientLeafShadowHandle_t m_ClientLeafShadowHandle;
		unsigned short			m_Flags;
		VMatrix					m_WorldToShadow;
		Vector2D				m_WorldSize;
		Vector					m_ShadowDir;	
		Vector					m_LastOrigin;
		QAngle					m_LastAngles;
		Vector					m_CurrentLightPos;	// When shadowing from local lights, stores the position of the currently shadowing light
        Vector					m_TargetLightPos;	// When shadowing from local lights, stores the position of the new shadowing light
        float					m_LightPosLerp;		// Lerp progress when going from current to target light
		TextureHandle_t			m_ShadowTexture;
		CTextureReference		m_ShadowDepthTexture;
		int						m_nRenderFrame;
		EHANDLE					m_hTargetEntity;
	};

private:
	// Shadow update functions
	void UpdateStudioShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle );
	void UpdateBrushShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle );
	void UpdateShadow( ClientShadowHandle_t handle, bool force );

	// Gets the entity whose shadow this shadow will render into
	IClientRenderable *GetParentShadowEntity( ClientShadowHandle_t handle );

	// Adds the child bounds to the bounding box
	void AddChildBounds( matrix3x4_t &matWorldToBBox, IClientRenderable* pParent, Vector &vecMins, Vector &vecMaxs );

	// Compute a bounds for the entity + children
	void ComputeHierarchicalBounds( IClientRenderable *pRenderable, Vector &vecMins, Vector &vecMaxs );

	// Builds matrices transforming from world space to shadow space
	void BuildGeneralWorldToShadowMatrix( VMatrix& matWorldToShadow,
		const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec );

	void BuildWorldToShadowMatrix( VMatrix& matWorldToShadow, const Vector& origin, const Quaternion& quatOrientation );

	void BuildPerspectiveWorldToFlashlightMatrix( VMatrix& matWorldToShadow, const FlashlightState_t &flashlightState );

	// Update a shadow
	void UpdateProjectedTextureInternal( ClientShadowHandle_t handle, bool force );

	// Compute the shadow origin and attenuation start distance
	float ComputeLocalShadowOrigin( IClientRenderable* pRenderable, 
		const Vector& mins, const Vector& maxs, const Vector& localShadowDir, float backupFactor, Vector& origin );

	// Remove a shadow from the dirty list
	void RemoveShadowFromDirtyList( ClientShadowHandle_t handle );

	// NOTE: this will ONLY return SHADOWS_NONE, SHADOWS_SIMPLE, or SHADOW_RENDER_TO_TEXTURE.
	ShadowType_t GetActualShadowCastType( ClientShadowHandle_t handle ) const;
	ShadowType_t GetActualShadowCastType( IClientRenderable *pRenderable ) const;

	// Builds a simple blobby shadow
	void BuildOrthoShadow( IClientRenderable* pRenderable, ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs);

	// Builds a more complex shadow...
	void BuildRenderToTextureShadow( IClientRenderable* pRenderable, 
			ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs );

	// Build a projected-texture flashlight
	void BuildFlashlight( ClientShadowHandle_t handle );

	// Does all the lovely stuff we need to do to have render-to-texture shadows
	void SetupRenderToTextureShadow( ClientShadowHandle_t h );
	void CleanUpRenderToTextureShadow( ClientShadowHandle_t h );

	// Compute the extra shadow planes
	void ComputeExtraClipPlanes( IClientRenderable* pRenderable, 
		ClientShadowHandle_t handle, const Vector* vec, 
		const Vector& mins, const Vector& maxs, const Vector& localShadowDir );

	// Set extra clip planes related to shadows...
	void ClearExtraClipPlanes( ClientShadowHandle_t h );
	void AddExtraClipPlane( ClientShadowHandle_t h, const Vector& normal, float dist );

	// Cull if the origin is on the wrong side of a shadow clip plane....
	bool CullReceiver( ClientShadowHandle_t handle, IClientRenderable* pRenderable, IClientRenderable* pSourceRenderable );

	bool ComputeSeparatingPlane( IClientRenderable* pRend1, IClientRenderable* pRend2, cplane_t* pPlane );

	// Causes all shadows to be re-updated
	void UpdateAllShadows();

	// One of these gets called with every shadow that potentially will need to re-render
	bool DrawRenderToTextureShadow( unsigned short clientShadowHandle, float flArea );
	void DrawRenderToTextureShadowLOD( unsigned short clientShadowHandle );

	// Draws all children shadows into our own
	bool DrawShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild = false );

	// Setup stage for threading
	bool BuildSetupListForRenderToTextureShadow( unsigned short clientShadowHandle, float flArea );
	bool BuildSetupShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild = false );

	// Computes + sets the render-to-texture texcoords
	void SetRenderToTextureShadowTexCoords( ShadowHandle_t handle, int x, int y, int w, int h );

	// Visualization....
	void DrawRenderToTextureDebugInfo( IClientRenderable* pRenderable, const Vector& mins, const Vector& maxs );

	// Advance frame
	void AdvanceFrame();

	// Returns renderable-specific shadow info
	float GetShadowDistance( IClientRenderable *pRenderable ) const;
	const Vector &GetShadowDirection( IClientRenderable *pRenderable ) const;
	const Vector &GetShadowDirection( ClientShadowHandle_t shadowHandle ) const;

	// Initialize, shutdown render-to-texture shadows
	void InitDepthTextureShadows();
	void ShutdownDepthTextureShadows();

	// Initialize, shutdown render-to-texture shadows
	void InitRenderToTextureShadows();
	void ShutdownRenderToTextureShadows();

	static bool ShadowHandleCompareFunc( const ClientShadowHandle_t& lhs, const ClientShadowHandle_t& rhs )
	{
		return lhs < rhs;
	}

	ClientShadowHandle_t CreateProjectedTexture( ClientEntityHandle_t entity, int flags );

	// Lock down the usage of a shadow depth texture...must be unlocked use on subsequent views / frames
	bool	LockShadowDepthTexture( CTextureReference *shadowDepthTexture );
	void	UnlockAllShadowDepthTextures();

	// Set and clear flashlight target renderable
	void	SetFlashlightTarget( ClientShadowHandle_t shadowHandle, EHANDLE targetEntity );

	// Set flashlight light world flag
	void	SetFlashlightLightWorld( ClientShadowHandle_t shadowHandle, bool bLightWorld );

	bool	IsFlashlightTarget( ClientShadowHandle_t shadowHandle, IClientRenderable *pRenderable );

	// Builds a list of active shadows requiring shadow depth renders
	int		BuildActiveShadowDepthList( const CViewSetup &viewSetup, int nMaxDepthShadows, ClientShadowHandle_t *pActiveDepthShadows );

	// Sets the view's active flashlight render state
	void	SetViewFlashlightState( int nActiveFlashlightCount, ClientShadowHandle_t* pActiveFlashlights );
	void	UpdateDirtyShadow( ClientShadowHandle_t handle );
    void	UpdateShadowDirectionFromLocalLightSource( ClientShadowHandle_t shadowHandle );

private:
	Vector	m_SimpleShadowDir;
	color32	m_AmbientLightColor;
	CMaterialReference m_SimpleShadow;
	CMaterialReference m_RenderShadow;
	CMaterialReference m_RenderModelShadow;
	CTextureReference m_DummyColorTexture;
	CUtlLinkedList< ClientShadow_t, ClientShadowHandle_t >	m_Shadows;
	CTextureAllocator m_ShadowAllocator;

	bool m_RenderToTextureActive;
	bool m_bRenderTargetNeedsClear;
	bool m_bUpdatingDirtyShadows;
	bool m_bThreaded;
	float m_flShadowCastDist;
	float m_flMinShadowArea;
	CUtlRBTree< ClientShadowHandle_t, unsigned short >	m_DirtyShadows;
	CUtlVector< ClientShadowHandle_t > m_TransparentShadows;

	// These members maintain current state of depth texturing (size and global active state)
	// If either changes in a frame, PreRender() will catch it and do the appropriate allocation, deallocation or reallocation
	bool m_bDepthTextureActive;
	int m_nDepthTextureResolution; // Assume square (height == width)
	bool m_bShadowFromWorldLights;

	CUtlVector< CTextureReference > m_DepthTextureCache;
	CUtlVector< bool > m_DepthTextureCacheLocks;
	int	m_nMaxDepthTextureShadows;

	friend class CVisibleShadowList;
	friend class CVisibleShadowFrustumList;
};

//-----------------------------------------------------------------------------
// Singleton
//-----------------------------------------------------------------------------
static CClientShadowMgr s_ClientShadowMgr;
IClientShadowMgr* g_pClientShadowMgr = &s_ClientShadowMgr;

//-----------------------------------------------------------------------------
// Builds a list of potential shadows that lie within our PVS + view frustum
//-----------------------------------------------------------------------------
struct VisibleShadowInfo_t
{
	ClientShadowHandle_t	m_hShadow;
	float					m_flArea;
	Vector					m_vecAbsCenter;
};

class CVisibleShadowList : public IClientLeafShadowEnum
{
public:

	CVisibleShadowList();
	int FindShadows( const CViewSetup *pView, int nLeafCount, LeafIndex_t *pLeafList );
	int GetVisibleShadowCount() const;

	const VisibleShadowInfo_t &GetVisibleShadow( int i ) const;

private:
	void EnumShadow( unsigned short clientShadowHandle );
	float ComputeScreenArea( const Vector &vecCenter, float r ) const;
	void PrioritySort();

	CUtlVector<VisibleShadowInfo_t> m_ShadowsInView;
	CUtlVector<int>	m_PriorityIndex;
};

//-----------------------------------------------------------------------------
// Singleton instances of shadow and shadow frustum lists
//-----------------------------------------------------------------------------
static CVisibleShadowList			s_VisibleShadowList;

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static CUtlVector<C_BaseAnimating *> s_NPCShadowBoneSetups;
static CUtlVector<C_BaseAnimating *> s_NonNPCShadowBoneSetups;

//-----------------------------------------------------------------------------
// CVisibleShadowList - Constructor and Accessors
//-----------------------------------------------------------------------------
CVisibleShadowList::CVisibleShadowList() : m_ShadowsInView( 0, 64 ), m_PriorityIndex( 0, 64 ) 
{
}

int CVisibleShadowList::GetVisibleShadowCount() const
{
	return m_ShadowsInView.Count();
}

const VisibleShadowInfo_t &CVisibleShadowList::GetVisibleShadow( int i ) const
{
	return m_ShadowsInView[m_PriorityIndex[i]];
}

//-----------------------------------------------------------------------------
// CVisibleShadowList - Computes approximate screen area of the shadow
//-----------------------------------------------------------------------------
float CVisibleShadowList::ComputeScreenArea( const Vector &vecCenter, float r ) const
{
	CMatRenderContextPtr pRenderContext( materials );
	float flScreenDiameter = pRenderContext->ComputePixelDiameterOfSphere( vecCenter, r );
	return flScreenDiameter * flScreenDiameter;
}

//-----------------------------------------------------------------------------
// CVisibleShadowList - Visits every shadow in the list of leaves
//-----------------------------------------------------------------------------
void CVisibleShadowList::EnumShadow( unsigned short clientShadowHandle )
{
	CClientShadowMgr::ClientShadow_t& shadow = s_ClientShadowMgr.m_Shadows[clientShadowHandle];

	// Don't bother if we rendered it this frame, no matter which view it was rendered for
	if ( shadow.m_nRenderFrame == gpGlobals->framecount )
		return;

	// We don't need to bother with it if it's not render-to-texture
	if ( s_ClientShadowMgr.GetActualShadowCastType( clientShadowHandle ) != SHADOWS_RENDER_TO_TEXTURE )
		return;

	// Don't bother with it if the shadow is totally transparent
	const ShadowInfo_t &shadowInfo = shadowmgr->GetInfo( shadow.m_ShadowHandle );
	if ( shadowInfo.m_FalloffBias == 255 )
		return;

	IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
	Assert( pRenderable );

	// Don't bother with children of hierarchy; they will be drawn with their parents
	if ( s_ClientShadowMgr.ShouldUseParentShadow( pRenderable ) || s_ClientShadowMgr.WillParentRenderBlobbyShadow( pRenderable ) )
		return;

	// Compute a sphere surrounding the shadow
	// FIXME: This doesn't account for children of hierarchy... too bad!
	Vector vecAbsCenter;
	float flRadius;
	s_ClientShadowMgr.ComputeBoundingSphere( pRenderable, vecAbsCenter, flRadius );

	// Compute a box surrounding the shadow
	Vector vecAbsMins, vecAbsMaxs;
	s_ClientShadowMgr.ComputeShadowBBox( pRenderable, shadow.m_ShadowHandle, vecAbsCenter, flRadius, &vecAbsMins, &vecAbsMaxs );

	// FIXME: Add distance check here?

	// Make sure it's in the frustum. If it isn't it's not interesting
	if (engine->CullBox( vecAbsMins, vecAbsMaxs ))
		return;

	int i = m_ShadowsInView.AddToTail( );
	VisibleShadowInfo_t &info = m_ShadowsInView[i];
	info.m_hShadow = clientShadowHandle;
	m_ShadowsInView[i].m_flArea = ComputeScreenArea( vecAbsCenter, flRadius );

	// Har, har. When water is rendering (or any multipass technique), 
	// we may well initially render from a viewpoint which doesn't include this shadow. 
	// That doesn't mean we shouldn't check it again though. Sucks that we need to compute
	// the sphere + bbox multiply times though.
	shadow.m_nRenderFrame = gpGlobals->framecount;
}

//-----------------------------------------------------------------------------
// CVisibleShadowList - Sort based on screen area/priority
//-----------------------------------------------------------------------------
void CVisibleShadowList::PrioritySort()
{
	int nCount = m_ShadowsInView.Count();
	m_PriorityIndex.EnsureCapacity( nCount );

	m_PriorityIndex.RemoveAll();

	int i, j;
	for ( i = 0; i < nCount; ++i )
	{
		m_PriorityIndex.AddToTail(i);
	}

	for ( i = 0; i < nCount - 1; ++i )
	{
		int nLargestInd = i;
		float flLargestArea = m_ShadowsInView[m_PriorityIndex[i]].m_flArea;
		for ( j = i + 1; j < nCount; ++j )
		{
			int nIndex = m_PriorityIndex[j];
			if ( flLargestArea < m_ShadowsInView[nIndex].m_flArea )
			{
				nLargestInd = j;
				flLargestArea = m_ShadowsInView[nIndex].m_flArea;
			}
		}
		::V_swap( m_PriorityIndex[i], m_PriorityIndex[nLargestInd] );
	}
}

//-----------------------------------------------------------------------------
// CVisibleShadowList - Main entry point for finding shadows in the leaf list
//-----------------------------------------------------------------------------
int CVisibleShadowList::FindShadows( const CViewSetup *pView, int nLeafCount, LeafIndex_t *pLeafList )
{
	VPROF_BUDGET( "CVisibleShadowList::FindShadows", VPROF_BUDGETGROUP_SHADOW_RENDERING );

	m_ShadowsInView.RemoveAll();
	ClientLeafSystem()->EnumerateShadowsInLeaves( nLeafCount, pLeafList, this );
	int nCount = m_ShadowsInView.Count();
	if (nCount != 0)
	{
		// Sort based on screen area/priority
		PrioritySort();
	}
	return nCount;
}

//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CClientShadowMgr::CClientShadowMgr() :
	m_DirtyShadows( 0, 0, ShadowHandleCompareFunc ),
	m_RenderToTextureActive( false ),
	m_bDepthTextureActive( false )
{
	m_nDepthTextureResolution = r_flashlightdepthres.GetInt();
	m_bThreaded = false;
	m_bShadowFromWorldLights = r_worldlight_castshadows.GetBool();
	
}

//-----------------------------------------------------------------------------
// Changes the shadow direction...
//-----------------------------------------------------------------------------
CON_COMMAND_F( r_shadowdir, "Set shadow direction", FCVAR_CHEAT )
{
	Vector dir;
	if ( args.ArgC() == 1 )
	{
		Vector dir = s_ClientShadowMgr.GetShadowDirection();
		Msg( "%.2f %.2f %.2f\n", dir.x, dir.y, dir.z );
		return;
	}

	if ( args.ArgC() == 4 )
	{
		dir.x = atof( args[1] );
		dir.y = atof( args[2] );
		dir.z = atof( args[3] );
		s_ClientShadowMgr.SetShadowDirection(dir);
	}
}

CON_COMMAND_F( r_shadowangles, "Set shadow angles", FCVAR_CHEAT )
{
	Vector dir;
	QAngle angles;
	if (args.ArgC() == 1)
	{
		Vector dir = s_ClientShadowMgr.GetShadowDirection();
		QAngle angles;
		VectorAngles( dir, angles );
		Msg( "%.2f %.2f %.2f\n", angles.x, angles.y, angles.z );
		return;
	}

	if (args.ArgC() == 4)
	{
		angles.x = atof( args[1] );
		angles.y = atof( args[2] );
		angles.z = atof( args[3] );
		AngleVectors( angles, &dir );
		s_ClientShadowMgr.SetShadowDirection(dir);
	}
}

CON_COMMAND_F( r_shadowcolor, "Set shadow color", FCVAR_CHEAT )
{
	if (args.ArgC() == 1)
	{
		unsigned char r, g, b;
		s_ClientShadowMgr.GetShadowColor( &r, &g, &b );
		Msg( "Shadow color %d %d %d\n", r, g, b );
		return;
	}

	if (args.ArgC() == 4)
	{
		int r = atoi( args[1] );
		int g = atoi( args[2] );
		int b = atoi( args[3] );
		s_ClientShadowMgr.SetShadowColor(r, g, b);
	}
}

CON_COMMAND_F( r_shadowdist, "Set shadow distance", FCVAR_CHEAT )
{
	if (args.ArgC() == 1)
	{
		float flDist = s_ClientShadowMgr.GetShadowDistance( );
		Msg( "Shadow distance %.2f\n", flDist );
		return;
	}

	if (args.ArgC() == 2)
	{
		float flDistance = atof( args[1] );
		s_ClientShadowMgr.SetShadowDistance( flDistance );
	}
}

CON_COMMAND_F( r_shadowblobbycutoff, "some shadow stuff", FCVAR_CHEAT )
{
	if (args.ArgC() == 1)
	{
		float flArea = s_ClientShadowMgr.GetBlobbyCutoffArea( );
		Msg( "Cutoff area %.2f\n", flArea );
		return;
	}

	if (args.ArgC() == 2)
	{
		float flArea = atof( args[1] );
		s_ClientShadowMgr.SetShadowBlobbyCutoffArea( flArea );
	}
}

static void ShadowRestoreFunc( int nChangeFlags )
{
	s_ClientShadowMgr.RestoreRenderState();
}

//-----------------------------------------------------------------------------
// Initialization, shutdown
//-----------------------------------------------------------------------------
bool CClientShadowMgr::Init()
{
	m_bRenderTargetNeedsClear = false;
	m_SimpleShadow.Init( "decals/simpleshadow", TEXTURE_GROUP_DECAL );

	Vector dir( 0.1, 0.1, -1 );
	SetShadowDirection(dir);
	SetShadowDistance( 50 );

	SetShadowBlobbyCutoffArea( 0.005 );

	bool bTools = CommandLine()->CheckParm( "-tools" ) != NULL;
	m_nMaxDepthTextureShadows = bTools ? 4 : 1;	// Just one shadow depth texture in games, more in tools

	bool bLowEnd = ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 );

	if ( !bLowEnd && r_shadowrendertotexture.GetBool() )
	{
		InitRenderToTextureShadows();
	}

	// If someone turned shadow depth mapping on but we can't do it, force it off
	if ( r_flashlightdepthtexture.GetBool() && !materials->SupportsShadowDepthTextures() )
	{
		r_flashlightdepthtexture.SetValue( 0 );
		ShutdownDepthTextureShadows();	
	}

	if ( !bLowEnd && r_flashlightdepthtexture.GetBool() )
	{
		InitDepthTextureShadows();
	}

	materials->AddRestoreFunc( ShadowRestoreFunc );

	return true;
}

void CClientShadowMgr::Shutdown()
{
	m_SimpleShadow.Shutdown();
	m_Shadows.RemoveAll();
	ShutdownRenderToTextureShadows();

	ShutdownDepthTextureShadows();

	materials->RemoveRestoreFunc( ShadowRestoreFunc );
}

//-----------------------------------------------------------------------------
// Initialize, shutdown depth-texture shadows
//-----------------------------------------------------------------------------
void CClientShadowMgr::InitDepthTextureShadows()
{
	VPROF_BUDGET( "CClientShadowMgr::InitDepthTextureShadows", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );

	if( !m_bDepthTextureActive )
	{
		m_bDepthTextureActive = true;

		ImageFormat dstFormat  = materials->GetShadowDepthTextureFormat();	// Vendor-dependent depth texture format
#if !defined( _X360 )
		ImageFormat nullFormat = materials->GetNullTextureFormat();			// Vendor-dependent null texture format (takes as little memory as possible)
#endif
		materials->BeginRenderTargetAllocation();

#if defined( _X360 )
		// For the 360, we'll be rendering depth directly into the dummy depth and Resolve()ing to the depth texture.
		// only need the dummy surface, don't care about color results
		m_DummyColorTexture.InitRenderTargetTexture( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), RT_SIZE_OFFSCREEN, IMAGE_FORMAT_BGR565, MATERIAL_RT_DEPTH_SHARED, false, "_rt_ShadowDummy" );
		m_DummyColorTexture.InitRenderTargetSurface( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), IMAGE_FORMAT_BGR565, true );
#else
		m_DummyColorTexture.InitRenderTarget( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), RT_SIZE_OFFSCREEN, nullFormat, MATERIAL_RT_DEPTH_NONE, false, "_rt_ShadowDummy" );
#endif

		// Create some number of depth-stencil textures
		m_DepthTextureCache.Purge();
		m_DepthTextureCacheLocks.Purge();
		for( int i=0; i < m_nMaxDepthTextureShadows; i++ )
		{
			CTextureReference depthTex;	// Depth-stencil surface
			bool bFalse = false;

			char strRTName[64];
			Q_snprintf( strRTName, ARRAYSIZE( strRTName ), "_rt_ShadowDepthTexture_%d", i );

#if defined( _X360 )
			// create a render target to use as a resolve target to get the shared depth buffer
			// surface is effectively never used
			depthTex.InitRenderTargetTexture( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_OFFSCREEN, dstFormat, MATERIAL_RT_DEPTH_NONE, false, strRTName );
			depthTex.InitRenderTargetSurface( 1, 1, dstFormat, false );
#else
			depthTex.InitRenderTarget( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_OFFSCREEN, dstFormat, MATERIAL_RT_DEPTH_NONE, false, strRTName );
#endif

			if ( i == 0 )
			{
				// Shadow may be resized during allocation (due to resolution constraints etc)
				m_nDepthTextureResolution = depthTex->GetActualWidth();
				r_flashlightdepthres.SetValue( m_nDepthTextureResolution );
			}

			m_DepthTextureCache.AddToTail( depthTex );
			m_DepthTextureCacheLocks.AddToTail( bFalse );
		}

		materials->EndRenderTargetAllocation();
	}
}

void CClientShadowMgr::ShutdownDepthTextureShadows() 
{
	if( m_bDepthTextureActive )
	{
		// Shut down the dummy texture
		m_DummyColorTexture.Shutdown();

		while( m_DepthTextureCache.Count() )
		{
			m_DepthTextureCache[ m_DepthTextureCache.Count()-1 ].Shutdown();

			m_DepthTextureCacheLocks.Remove( m_DepthTextureCache.Count()-1 );
			m_DepthTextureCache.Remove( m_DepthTextureCache.Count()-1 );
		}

		m_bDepthTextureActive = false;
	}
}

//-----------------------------------------------------------------------------
// Initialize, shutdown render-to-texture shadows
//-----------------------------------------------------------------------------
void CClientShadowMgr::InitRenderToTextureShadows()
{
	if ( !m_RenderToTextureActive )
	{
		m_RenderToTextureActive = true;
		m_RenderShadow.Init( "decals/rendershadow", TEXTURE_GROUP_DECAL );
		m_RenderModelShadow.Init( "decals/rendermodelshadow", TEXTURE_GROUP_DECAL );
		m_ShadowAllocator.Init();

		m_ShadowAllocator.Reset();
		m_bRenderTargetNeedsClear = true;

		float fr = (float)m_AmbientLightColor.r / 255.0f;
		float fg = (float)m_AmbientLightColor.g / 255.0f;
		float fb = (float)m_AmbientLightColor.b / 255.0f;
		m_RenderShadow->ColorModulate( fr, fg, fb );
		m_RenderModelShadow->ColorModulate( fr, fg, fb );

		// Iterate over all existing textures and allocate shadow textures
		for (ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
		{
			ClientShadow_t& shadow = m_Shadows[i];
			if ( shadow.m_Flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE )
			{
				SetupRenderToTextureShadow( i );
				MarkRenderToTextureShadowDirty( i );

				// Switch the material to use render-to-texture shadows
				shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_RenderShadow, m_RenderModelShadow, (void*)(uintp)i );
			}
		}
	}
}

void CClientShadowMgr::ShutdownRenderToTextureShadows()
{
	if (m_RenderToTextureActive)
	{
		// Iterate over all existing textures and deallocate shadow textures
		for (ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
		{
			CleanUpRenderToTextureShadow( i );

			// Switch the material to use blobby shadows
			ClientShadow_t& shadow = m_Shadows[i];
			shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_SimpleShadow, m_SimpleShadow, (void*)CLIENTSHADOW_INVALID_HANDLE );
			shadowmgr->SetShadowTexCoord( shadow.m_ShadowHandle, 0, 0, 1, 1 );
			ClearExtraClipPlanes( i );
		}

		m_RenderShadow.Shutdown();
		m_RenderModelShadow.Shutdown();

		m_ShadowAllocator.DeallocateAllTextures();
		m_ShadowAllocator.Shutdown();

		// Cause the render target to go away
		materials->UncacheUnusedMaterials();

		m_RenderToTextureActive = false;
	}
}

//-----------------------------------------------------------------------------
// Sets the shadow color
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetShadowColor( unsigned char r, unsigned char g, unsigned char b )
{
	float fr = (float)r / 255.0f;
	float fg = (float)g / 255.0f;
	float fb = (float)b / 255.0f;

	// Hook the shadow color into the shadow materials
	m_SimpleShadow->ColorModulate( fr, fg, fb );

	if (m_RenderToTextureActive)
	{
		m_RenderShadow->ColorModulate( fr, fg, fb );
		m_RenderModelShadow->ColorModulate( fr, fg, fb );
	}

	m_AmbientLightColor.r = r;
	m_AmbientLightColor.g = g;
	m_AmbientLightColor.b = b;
}

void CClientShadowMgr::GetShadowColor( unsigned char *r, unsigned char *g, unsigned char *b ) const
{
	*r = m_AmbientLightColor.r;
	*g = m_AmbientLightColor.g;
	*b = m_AmbientLightColor.b;
}

//-----------------------------------------------------------------------------
// Level init... get the shadow color
//-----------------------------------------------------------------------------
void CClientShadowMgr::LevelInitPreEntity()
{
	m_bUpdatingDirtyShadows = false;

	Vector ambientColor;
	engine->GetAmbientLightColor( ambientColor );
	ambientColor *= 3;
	ambientColor += Vector( 0.3f, 0.3f, 0.3f );

	unsigned char r = ambientColor[0] > 1.0 ? 255 : 255 * ambientColor[0];
	unsigned char g = ambientColor[1] > 1.0 ? 255 : 255 * ambientColor[1];
	unsigned char b = ambientColor[2] > 1.0 ? 255 : 255 * ambientColor[2];

	SetShadowColor(r, g, b);

	// Set up the texture allocator
	if ( m_RenderToTextureActive )
	{
		m_ShadowAllocator.Reset();
		m_bRenderTargetNeedsClear = true;
	}
}

//-----------------------------------------------------------------------------
// Clean up all shadows
//-----------------------------------------------------------------------------
void CClientShadowMgr::LevelShutdownPostEntity()
{
	// All shadows *should* have been cleaned up when the entities went away
	// but, just in case....
	Assert( m_Shadows.Count() == 0 );

	ClientShadowHandle_t h = m_Shadows.Head();
	while (h != CLIENTSHADOW_INVALID_HANDLE)
	{
		ClientShadowHandle_t next = m_Shadows.Next(h);
		DestroyShadow( h );
		h = next;
	}

	// Deallocate all textures
	if (m_RenderToTextureActive)
	{
		m_ShadowAllocator.DeallocateAllTextures();
	}

	r_shadows_gamecontrol.SetValue( -1 );
}

//-----------------------------------------------------------------------------
// Deals with alt-tab
//-----------------------------------------------------------------------------
void CClientShadowMgr::RestoreRenderState()
{
	// Mark all shadows dirty; they need to regenerate their state
	ClientShadowHandle_t h;
	for ( h = m_Shadows.Head(); h != m_Shadows.InvalidIndex(); h = m_Shadows.Next(h) )
	{
		m_Shadows[h].m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;
	}

	SetShadowColor( m_AmbientLightColor.r, m_AmbientLightColor.g, m_AmbientLightColor.b );
	m_bRenderTargetNeedsClear = true;
}

//-----------------------------------------------------------------------------
// Does all the lovely stuff we need to do to have render-to-texture shadows
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetupRenderToTextureShadow( ClientShadowHandle_t h )
{
	// First, compute how much texture memory we want to use.
	ClientShadow_t& shadow = m_Shadows[h];
	
	IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
	if ( !pRenderable )
		return;

	Vector mins, maxs;
	pRenderable->GetShadowRenderBounds( mins, maxs, GetActualShadowCastType( h ) );

	// Compute the maximum dimension
	Vector size;
	VectorSubtract( maxs, mins, size );
	float maxSize = MAX( size.x, size.y );
	maxSize = MAX( maxSize, size.z );

	// Figure out the texture size
	// For now, we're going to assume a fixed number of shadow texels
	// per shadow-caster size; add in some extra space at the boundary.
	int texelCount = TEXEL_SIZE_PER_CASTER_SIZE * maxSize;
	
	// Pick the first power of 2 larger...
	int textureSize = 1;
	while (textureSize < texelCount)
	{
		textureSize <<= 1;
	}

	shadow.m_ShadowTexture = m_ShadowAllocator.AllocateTexture( textureSize, textureSize );
}

void CClientShadowMgr::CleanUpRenderToTextureShadow( ClientShadowHandle_t h )
{
	ClientShadow_t& shadow = m_Shadows[h];
	if (m_RenderToTextureActive && (shadow.m_Flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE))
	{
		m_ShadowAllocator.DeallocateTexture( shadow.m_ShadowTexture );
		shadow.m_ShadowTexture = INVALID_TEXTURE_HANDLE;
	}
}

//-----------------------------------------------------------------------------
// Causes all shadows to be re-updated
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateAllShadows()
{
	for ( ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
	{
		ClientShadow_t& shadow = m_Shadows[i];

		// Don't bother with flashlights
		if ( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) != 0 )
			continue;

		IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
		if ( !pRenderable )
			continue;

		Assert( pRenderable->GetShadowHandle() == i );
		AddToDirtyShadowList( pRenderable, true );
	}
}

//-----------------------------------------------------------------------------
// Sets the shadow direction
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetShadowDirection( const Vector& dir )
{
	VectorCopy( dir, m_SimpleShadowDir );
	VectorNormalize( m_SimpleShadowDir );

	if ( m_RenderToTextureActive )
	{
		UpdateAllShadows();
	}
}

const Vector &CClientShadowMgr::GetShadowDirection() const
{
	// This will cause blobby shadows to always project straight down
	static Vector s_vecDown( 0, 0, -1 );
	if ( !m_RenderToTextureActive )
		return s_vecDown;

	return m_SimpleShadowDir;
}

//-----------------------------------------------------------------------------
// Gets shadow information for a particular renderable
//-----------------------------------------------------------------------------
float CClientShadowMgr::GetShadowDistance( IClientRenderable *pRenderable ) const
{
	float flDist = m_flShadowCastDist;

	// Allow the renderable to override the default
	pRenderable->GetShadowCastDistance( &flDist, GetActualShadowCastType( pRenderable ) );

	return flDist;
}

const Vector &CClientShadowMgr::GetShadowDirection( IClientRenderable *pRenderable ) const
{
	Vector &vecResult = AllocTempVector();
	vecResult = GetShadowDirection();

	// Allow the renderable to override the default
	pRenderable->GetShadowCastDirection( &vecResult, GetActualShadowCastType( pRenderable ) );

	return vecResult;
}

//-----------------------------------------------------------------------------
// Sets the shadow distance
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetShadowDistance( float flMaxDistance )
{
	m_flShadowCastDist = flMaxDistance;
	UpdateAllShadows();
}

float CClientShadowMgr::GetShadowDistance( ) const
{
	return m_flShadowCastDist;
}

//-----------------------------------------------------------------------------
// Sets the screen area at which blobby shadows are always used
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetShadowBlobbyCutoffArea( float flMinArea )
{
	m_flMinShadowArea = flMinArea;
}

float CClientShadowMgr::GetBlobbyCutoffArea( ) const
{
	return m_flMinShadowArea;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetFalloffBias( ClientShadowHandle_t handle, unsigned char ucBias )
{
	shadowmgr->SetFalloffBias( m_Shadows[handle].m_ShadowHandle, ucBias );
}

//-----------------------------------------------------------------------------
// Returns the shadow texture
//-----------------------------------------------------------------------------
ITexture* CClientShadowMgr::GetShadowTexture( unsigned short h )
{
	return m_ShadowAllocator.GetTexture();
}

//-----------------------------------------------------------------------------
// Returns information needed by the model proxy
//-----------------------------------------------------------------------------
const ShadowInfo_t& CClientShadowMgr::GetShadowInfo( ClientShadowHandle_t h )
{
	return shadowmgr->GetInfo( m_Shadows[h].m_ShadowHandle );
}

//-----------------------------------------------------------------------------
// Renders the shadow texture to screen...
//-----------------------------------------------------------------------------
void CClientShadowMgr::RenderShadowTexture( int w, int h )
{
	if (m_RenderToTextureActive)
	{
		CMatRenderContextPtr pRenderContext( materials );
		pRenderContext->Bind( m_RenderShadow );
		IMesh* pMesh = pRenderContext->GetDynamicMesh( true );

		CMeshBuilder meshBuilder;
		meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );

		meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
		meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
		meshBuilder.Color4ub( 0, 0, 0, 0 );
		meshBuilder.AdvanceVertex();

		meshBuilder.Position3f( w, 0.0f, 0.0f );
		meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
		meshBuilder.Color4ub( 0, 0, 0, 0 );
		meshBuilder.AdvanceVertex();

		meshBuilder.Position3f( w, h, 0.0f );
		meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
		meshBuilder.Color4ub( 0, 0, 0, 0 );
		meshBuilder.AdvanceVertex();

		meshBuilder.Position3f( 0.0f, h, 0.0f );
		meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
		meshBuilder.Color4ub( 0, 0, 0, 0 );
		meshBuilder.AdvanceVertex();

		meshBuilder.End();
		pMesh->Draw();
	}
}

//-----------------------------------------------------------------------------
// Create/destroy a shadow
//-----------------------------------------------------------------------------
ClientShadowHandle_t CClientShadowMgr::CreateProjectedTexture( ClientEntityHandle_t entity, int flags )
{
	// We need to know if it's a brush model for shadows
	if( !( flags & SHADOW_FLAGS_FLASHLIGHT ) )
	{
		IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( entity );
		if ( !pRenderable )
			return m_Shadows.InvalidIndex();

		int modelType = modelinfo->GetModelType( pRenderable->GetModel() );
		if (modelType == mod_brush)
		{
			flags |= SHADOW_FLAGS_BRUSH_MODEL;
		}
	}

	ClientShadowHandle_t h = m_Shadows.AddToTail();
	ClientShadow_t& shadow = m_Shadows[h];
	shadow.m_Entity = entity;
	shadow.m_ClientLeafShadowHandle = ClientLeafSystem()->AddShadow( h, flags );
	shadow.m_Flags = flags;
	shadow.m_nRenderFrame = -1;
	shadow.m_ShadowDir = GetShadowDirection();
    shadow.m_CurrentLightPos.Init( FLT_MAX, FLT_MAX, FLT_MAX );
    shadow.m_TargetLightPos.Init( FLT_MAX, FLT_MAX, FLT_MAX );
    shadow.m_LightPosLerp = FLT_MAX;
	shadow.m_LastOrigin.Init( FLT_MAX, FLT_MAX, FLT_MAX );
	shadow.m_LastAngles.Init( FLT_MAX, FLT_MAX, FLT_MAX );
	Assert( ( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 ) != 
			( ( shadow.m_Flags & SHADOW_FLAGS_SHADOW ) == 0 ) );

	// Set up the flags....
	IMaterial* pShadowMaterial = m_SimpleShadow;
	IMaterial* pShadowModelMaterial = m_SimpleShadow;
	void* pShadowProxyData = (void*)CLIENTSHADOW_INVALID_HANDLE;

	if ( m_RenderToTextureActive && (flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE) )
	{
		SetupRenderToTextureShadow(h);

		pShadowMaterial = m_RenderShadow;
		pShadowModelMaterial = m_RenderModelShadow;
		pShadowProxyData = (void*)(uintp)h;
	}

	if( flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE )
	{
		pShadowMaterial = m_RenderShadow;
		pShadowModelMaterial = m_RenderModelShadow;
		pShadowProxyData = (void*)(uintp)h;
	}

	int createShadowFlags;
	if( flags & SHADOW_FLAGS_FLASHLIGHT )
	{
		// don't use SHADOW_CACHE_VERTS with projective lightsources since we expect that they will change every frame.
		// FIXME: might want to make it cache optionally if it's an entity light that is static.
		createShadowFlags = SHADOW_FLASHLIGHT;
	}
	else
	{
		createShadowFlags = SHADOW_CACHE_VERTS;
	}
	shadow.m_ShadowHandle = shadowmgr->CreateShadowEx( pShadowMaterial, pShadowModelMaterial, pShadowProxyData, createShadowFlags );
	return h;
}

ClientShadowHandle_t CClientShadowMgr::CreateFlashlight( const FlashlightState_t &lightState )
{
	// We don't really need a model entity handle for a projective light source, so use an invalid one.
	static ClientEntityHandle_t invalidHandle = INVALID_CLIENTENTITY_HANDLE;

	int shadowFlags = SHADOW_FLAGS_FLASHLIGHT | SHADOW_FLAGS_LIGHT_WORLD;
	if( lightState.m_bEnableShadows && r_flashlightdepthtexture.GetBool() )
	{
		shadowFlags |= SHADOW_FLAGS_USE_DEPTH_TEXTURE;
	}

	ClientShadowHandle_t shadowHandle = CreateProjectedTexture( invalidHandle, shadowFlags );

	UpdateFlashlightState( shadowHandle, lightState );
	UpdateProjectedTexture( shadowHandle, true );
	return shadowHandle;
}
		 
ClientShadowHandle_t CClientShadowMgr::CreateShadow( ClientEntityHandle_t entity, int flags )
{
	// We don't really need a model entity handle for a projective light source, so use an invalid one.
	flags &= ~SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK;
	flags |= SHADOW_FLAGS_SHADOW | SHADOW_FLAGS_TEXTURE_DIRTY;
	ClientShadowHandle_t shadowHandle = CreateProjectedTexture( entity, flags );

	IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( entity );
	if ( pRenderable )
	{
		Assert( !pRenderable->IsShadowDirty( ) );
		pRenderable->MarkShadowDirty( true );
	}

	// NOTE: We *have* to call the version that takes a shadow handle
	// even if we have an entity because this entity hasn't set its shadow handle yet
	AddToDirtyShadowList( shadowHandle, true );
	return shadowHandle;
}

//-----------------------------------------------------------------------------
// Updates the flashlight direction and re-computes surfaces it should lie on
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateFlashlightState( ClientShadowHandle_t shadowHandle, const FlashlightState_t &flashlightState )
{
	VPROF_BUDGET( "CClientShadowMgr::UpdateFlashlightState", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );

	BuildPerspectiveWorldToFlashlightMatrix( m_Shadows[shadowHandle].m_WorldToShadow, flashlightState );
											
	shadowmgr->UpdateFlashlightState( m_Shadows[shadowHandle].m_ShadowHandle, flashlightState );
}

void CClientShadowMgr::DestroyFlashlight( ClientShadowHandle_t shadowHandle )
{
	DestroyShadow( shadowHandle );
}

//-----------------------------------------------------------------------------
// Remove a shadow from the dirty list
//-----------------------------------------------------------------------------
void CClientShadowMgr::RemoveShadowFromDirtyList( ClientShadowHandle_t handle )
{
	int idx = m_DirtyShadows.Find( handle );
	if ( idx != m_DirtyShadows.InvalidIndex() )
	{
		// Clean up the shadow update bit.
		IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[handle].m_Entity );
		if ( pRenderable )
		{
			pRenderable->MarkShadowDirty( false );
		}
		m_DirtyShadows.RemoveAt( idx );
	}
}

//-----------------------------------------------------------------------------
// Remove a shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::DestroyShadow( ClientShadowHandle_t handle )
{
	Assert( m_Shadows.IsValidIndex(handle) );
	RemoveShadowFromDirtyList( handle );
	shadowmgr->DestroyShadow( m_Shadows[handle].m_ShadowHandle );
	ClientLeafSystem()->RemoveShadow( m_Shadows[handle].m_ClientLeafShadowHandle );
	CleanUpRenderToTextureShadow( handle );
	m_Shadows.Remove(handle);
}

//-----------------------------------------------------------------------------
// Build the worldtotexture matrix
//-----------------------------------------------------------------------------
void CClientShadowMgr::BuildGeneralWorldToShadowMatrix( VMatrix& matWorldToShadow,
	const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec )
{
	// We're assuming here that xvec + yvec aren't necessary perpendicular

	// The shadow->world matrix is pretty simple:
	// Just stick the origin in the translation component
	// and the vectors in the columns...
	matWorldToShadow.SetBasisVectors( xvec, yvec, dir );
	matWorldToShadow.SetTranslation( origin );
	matWorldToShadow[3][0] = matWorldToShadow[3][1] = matWorldToShadow[3][2] = 0.0f;
	matWorldToShadow[3][3] = 1.0f;

	// Now do a general inverse to get matWorldToShadow
	MatrixInverseGeneral( matWorldToShadow, matWorldToShadow );
}

void CClientShadowMgr::BuildWorldToShadowMatrix( VMatrix& matWorldToShadow,	const Vector& origin, const Quaternion& quatOrientation )
{
	// The shadow->world matrix is pretty simple:
	// Just stick the origin in the translation component
	// and the vectors in the columns...
	// The inverse of this transposes the rotational component
	// and the translational component =  - (rotation transpose) * origin

	matrix3x4_t matOrientation;											
	QuaternionMatrix( quatOrientation, matOrientation );		// Convert quat to matrix3x4
	PositionMatrix( vec3_origin, matOrientation );				// Zero out translation elements

	VMatrix matBasis( matOrientation );							// Convert matrix3x4 to VMatrix

	Vector vForward, vLeft, vUp;
	matBasis.GetBasisVectors( vForward, vLeft, vUp );
	matBasis.SetForward( vLeft );								// Bizarre vector flip inherited from earlier code, WTF?
	matBasis.SetLeft( vUp );
	matBasis.SetUp( vForward );
	matWorldToShadow = matBasis.Transpose();					// Transpose

	Vector translation;
	Vector3DMultiply( matWorldToShadow, origin, translation );

	translation *= -1.0f;
	matWorldToShadow.SetTranslation( translation );

	// The the bottom row.
	matWorldToShadow[3][0] = matWorldToShadow[3][1] = matWorldToShadow[3][2] = 0.0f;
	matWorldToShadow[3][3] = 1.0f;
}

void CClientShadowMgr::BuildPerspectiveWorldToFlashlightMatrix( VMatrix& matWorldToShadow, const FlashlightState_t &flashlightState )
{
	VPROF_BUDGET( "CClientShadowMgr::BuildPerspectiveWorldToFlashlightMatrix", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );

	// Buildworld to shadow matrix, then perspective projection and concatenate
	VMatrix matWorldToShadowView, matPerspective;
	BuildWorldToShadowMatrix( matWorldToShadowView, flashlightState.m_vecLightOrigin,
							  flashlightState.m_quatOrientation );

	MatrixBuildPerspective( matPerspective, flashlightState.m_fHorizontalFOVDegrees,
							flashlightState.m_fVerticalFOVDegrees,
							flashlightState.m_NearZ, flashlightState.m_FarZ );

	MatrixMultiply( matPerspective, matWorldToShadowView, matWorldToShadow );
}

//-----------------------------------------------------------------------------
// Compute the shadow origin and attenuation start distance
//-----------------------------------------------------------------------------
float CClientShadowMgr::ComputeLocalShadowOrigin( IClientRenderable* pRenderable, 
	const Vector& mins, const Vector& maxs, const Vector& localShadowDir, float backupFactor, Vector& origin )
{
	// Compute the centroid of the object...
	Vector vecCentroid;
	VectorAdd( mins, maxs, vecCentroid );
	vecCentroid *= 0.5f;

	Vector vecSize;
	VectorSubtract( maxs, mins, vecSize );
	float flRadius = vecSize.Length() * 0.5f;

	// NOTE: The *origin* of the shadow cast is a point on a line passing through
	// the centroid of the caster. The direction of this line is the shadow cast direction,
	// and the point on that line corresponds to the endpoint of the box that is
	// furthest *back* along the shadow direction

	// For the first point at which the shadow could possibly start falling off,
	// we need to use the point at which the ray described above leaves the
	// bounding sphere surrounding the entity. This is necessary because otherwise,
	// tall, thin objects would have their shadows appear + disappear as then spun about their origin

	// Figure out the corner corresponding to the min + max projection
	// along the shadow direction

	// We're basically finding the point on the cube that has the largest and smallest
	// dot product with the local shadow dir. Then we're taking the dot product
	// of that with the localShadowDir. lastly, we're subtracting out the
	// centroid projection to give us a distance along the localShadowDir to
	// the front and back of the cube along the direction of the ray.
	float centroidProjection = DotProduct( vecCentroid, localShadowDir );
	float minDist = -centroidProjection;
	for (int i = 0; i < 3; ++i)
	{
		if ( localShadowDir[i] > 0.0f )
		{
			minDist += localShadowDir[i] * mins[i];
		}
		else
		{
			minDist += localShadowDir[i] * maxs[i];
		}
	}

	minDist *= backupFactor;

	VectorMA( vecCentroid, minDist, localShadowDir, origin );

	return flRadius - minDist;
}

//-----------------------------------------------------------------------------
// Sorts the components of a vector
//-----------------------------------------------------------------------------
static inline void SortAbsVectorComponents( const Vector& src, int* pVecIdx )
{
	Vector absVec( fabs(src[0]), fabs(src[1]), fabs(src[2]) );

	int maxIdx = (absVec[0] > absVec[1]) ? 0 : 1;
	if (absVec[2] > absVec[maxIdx])
	{
		maxIdx = 2;
	}

	// always choose something right-handed....
	switch(	maxIdx )
	{
	case 0:
		pVecIdx[0] = 1;
		pVecIdx[1] = 2;
		pVecIdx[2] = 0;
		break;
	case 1:
		pVecIdx[0] = 2;
		pVecIdx[1] = 0;
		pVecIdx[2] = 1;
		break;
	case 2:
		pVecIdx[0] = 0;
		pVecIdx[1] = 1;
		pVecIdx[2] = 2;
		break;
	}
}

//-----------------------------------------------------------------------------
// Build the worldtotexture matrix
//-----------------------------------------------------------------------------
static void BuildWorldToTextureMatrix( const VMatrix& matWorldToShadow, 
							const Vector2D& size, VMatrix& matWorldToTexture )
{
	// Build a matrix that maps from shadow space to (u,v) coordinates
	VMatrix shadowToUnit;
	MatrixBuildScale( shadowToUnit, 1.0f / size.x, 1.0f / size.y, 1.0f );
	shadowToUnit[0][3] = shadowToUnit[1][3] = 0.5f;

	// Store off the world to (u,v) transformation
	MatrixMultiply( shadowToUnit, matWorldToShadow, matWorldToTexture );
}

static void BuildOrthoWorldToShadowMatrix( VMatrix& worldToShadow,
													 const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec )
{
	// This version is faster and assumes dir, xvec, yvec are perpendicular
	AssertFloatEquals( DotProduct( dir, xvec ), 0.0f, 1e-3 );
	AssertFloatEquals( DotProduct( dir, yvec ), 0.0f, 1e-3 );
	AssertFloatEquals( DotProduct( xvec, yvec ), 0.0f, 1e-3 );

	// The shadow->world matrix is pretty simple:
	// Just stick the origin in the translation component
	// and the vectors in the columns...
	// The inverse of this transposes the rotational component
	// and the translational component =  - (rotation transpose) * origin
	worldToShadow.SetBasisVectors( xvec, yvec, dir );
	MatrixTranspose( worldToShadow, worldToShadow );

	Vector translation;
	Vector3DMultiply( worldToShadow, origin, translation );

	translation *= -1.0f;
	worldToShadow.SetTranslation( translation );

	// The the bottom row.
	worldToShadow[3][0] = worldToShadow[3][1] = worldToShadow[3][2] = 0.0f;
	worldToShadow[3][3] = 1.0f;
}

//-----------------------------------------------------------------------------
// Set extra clip planes related to shadows...
//-----------------------------------------------------------------------------
void CClientShadowMgr::ClearExtraClipPlanes( ClientShadowHandle_t h )
{
	shadowmgr->ClearExtraClipPlanes( m_Shadows[h].m_ShadowHandle );
}

void CClientShadowMgr::AddExtraClipPlane( ClientShadowHandle_t h, const Vector& normal, float dist )
{
	shadowmgr->AddExtraClipPlane( m_Shadows[h].m_ShadowHandle, normal, dist );
}

//-----------------------------------------------------------------------------
// Compute the extra shadow planes
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeExtraClipPlanes( IClientRenderable* pRenderable, 
	ClientShadowHandle_t handle, const Vector* vec, 
	const Vector& mins, const Vector& maxs, const Vector& localShadowDir )
{
	// Compute the world-space position of the corner of the bounding box
	// that's got the highest dotproduct with the local shadow dir...
	Vector origin = pRenderable->GetRenderOrigin( );
	float dir[3];

	int i;
	for ( i = 0; i < 3; ++i )
	{
		if (localShadowDir[i] < 0.0f)
		{
			VectorMA( origin, maxs[i], vec[i], origin );
			dir[i] = 1;
		}
		else
		{
			VectorMA( origin, mins[i], vec[i], origin );
			dir[i] = -1;
		}
	}

	// Now that we have it, create 3 planes...
	Vector normal;
	ClearExtraClipPlanes(handle);
	for ( i = 0; i < 3; ++i )
	{
		VectorMultiply( vec[i], dir[i], normal );
		float dist = DotProduct( normal, origin );
		AddExtraClipPlane( handle, normal, dist );
	}

	ClientShadow_t& shadow = m_Shadows[handle];
	C_BaseEntity *pEntity = ClientEntityList().GetBaseEntityFromHandle( shadow.m_Entity );
	if ( pEntity && pEntity->m_bEnableRenderingClipPlane )
	{
		normal[ 0 ] = -pEntity->m_fRenderingClipPlane[ 0 ];
		normal[ 1 ] = -pEntity->m_fRenderingClipPlane[ 1 ];
		normal[ 2 ] = -pEntity->m_fRenderingClipPlane[ 2 ];
		AddExtraClipPlane( handle, normal, -pEntity->m_fRenderingClipPlane[ 3 ] - 0.5f );
	}
}

inline ShadowType_t CClientShadowMgr::GetActualShadowCastType( ClientShadowHandle_t handle ) const
{
	if ( handle == CLIENTSHADOW_INVALID_HANDLE )
	{
		return SHADOWS_NONE;
	}
	
	if ( m_Shadows[handle].m_Flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE )
	{
		return ( m_RenderToTextureActive ? SHADOWS_RENDER_TO_TEXTURE : SHADOWS_SIMPLE );
	}
	else if( m_Shadows[handle].m_Flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE )
	{
		return SHADOWS_RENDER_TO_DEPTH_TEXTURE;
	}
	else
	{
		return SHADOWS_SIMPLE;
	}
}

inline ShadowType_t CClientShadowMgr::GetActualShadowCastType( IClientRenderable *pEnt ) const
{
	return GetActualShadowCastType( pEnt->GetShadowHandle() );
}

//-----------------------------------------------------------------------------
// Adds a shadow to all leaves along a ray
//-----------------------------------------------------------------------------
class CShadowLeafEnum : public ISpatialLeafEnumerator
{
public:
	bool EnumerateLeaf( int leaf, int context )
	{
		m_LeafList.AddToTail( leaf );
		return true;
	}

	CUtlVectorFixedGrowable< int, 512 > m_LeafList;
};

//-----------------------------------------------------------------------------
// Builds a list of leaves inside the shadow volume
//-----------------------------------------------------------------------------
static void BuildShadowLeafList( CShadowLeafEnum *pEnum, const Vector& origin, 
	const Vector& dir, const Vector2D& size, float maxDist )
{
	Ray_t ray;
	VectorCopy( origin, ray.m_Start );
	VectorMultiply( dir, maxDist, ray.m_Delta );
	ray.m_StartOffset.Init( 0, 0, 0 );

	float flRadius = sqrt( size.x * size.x + size.y * size.y ) * 0.5f;
	ray.m_Extents.Init( flRadius, flRadius, flRadius );
	ray.m_IsRay = false;
	ray.m_IsSwept = true;

	ISpatialQuery* pQuery = engine->GetBSPTreeQuery();
	pQuery->EnumerateLeavesAlongRay( ray, pEnum, 0 );
}

//-----------------------------------------------------------------------------
// Builds a simple blobby shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::BuildOrthoShadow( IClientRenderable* pRenderable, 
		ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs)
{
	// Get the object's basis
	Vector vec[3];
	AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
	vec[1] *= -1.0f;

	Vector vecShadowDir = GetShadowDirection( handle );

	// Project the shadow casting direction into the space of the object
	Vector localShadowDir;
	localShadowDir[0] = DotProduct( vec[0], vecShadowDir );
	localShadowDir[1] = DotProduct( vec[1], vecShadowDir );
	localShadowDir[2] = DotProduct( vec[2], vecShadowDir );

	// Figure out which vector has the largest component perpendicular
	// to the shadow handle...
	// Sort by how perpendicular it is
	int vecIdx[3];
	SortAbsVectorComponents( localShadowDir, vecIdx );

	// Here's our shadow basis vectors; namely the ones that are
	// most perpendicular to the shadow casting direction
	Vector xvec = vec[vecIdx[0]];
	Vector yvec = vec[vecIdx[1]];

	// Project them into a plane perpendicular to the shadow direction
	xvec -= vecShadowDir * DotProduct( vecShadowDir, xvec );
	yvec -= vecShadowDir * DotProduct( vecShadowDir, yvec );
	VectorNormalize( xvec );
	VectorNormalize( yvec );

	// Compute the box size
	Vector boxSize;
	VectorSubtract( maxs, mins, boxSize );

	// We project the two longest sides into the vectors perpendicular
	// to the projection direction, then add in the projection of the perp direction
	Vector2D size( boxSize[vecIdx[0]], boxSize[vecIdx[1]] );
	size.x *= fabs( DotProduct( vec[vecIdx[0]], xvec ) );
	size.y *= fabs( DotProduct( vec[vecIdx[1]], yvec ) );

	// Add the third component into x and y
	size.x += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], xvec ) );
	size.y += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], yvec ) );

	// Bloat a bit, since the shadow wants to extend outside the model a bit
	size.x += 10.0f;
	size.y += 10.0f;

	// Clamp the minimum size
	Vector2DMax( size, Vector2D(10.0f, 10.0f), size );

	// Place the origin at the point with min dot product with shadow dir
	Vector org;
	float falloffStart = ComputeLocalShadowOrigin( pRenderable, mins, maxs, localShadowDir, 2.0f, org );

	// Transform the local origin into world coordinates
	Vector worldOrigin = pRenderable->GetRenderOrigin( );
	VectorMA( worldOrigin, org.x, vec[0], worldOrigin );
	VectorMA( worldOrigin, org.y, vec[1], worldOrigin );
	VectorMA( worldOrigin, org.z, vec[2], worldOrigin );

	// FUNKY: A trick to reduce annoying texelization artifacts!?
	float dx = 1.0f / TEXEL_SIZE_PER_CASTER_SIZE;
	worldOrigin.x = (int)(worldOrigin.x / dx) * dx;
	worldOrigin.y = (int)(worldOrigin.y / dx) * dx;
	worldOrigin.z = (int)(worldOrigin.z / dx) * dx;

	// NOTE: We gotta use the general matrix because xvec and yvec aren't perp
	VMatrix matWorldToShadow, matWorldToTexture;
	BuildGeneralWorldToShadowMatrix( m_Shadows[handle].m_WorldToShadow, worldOrigin, vecShadowDir, xvec, yvec );
	BuildWorldToTextureMatrix( m_Shadows[handle].m_WorldToShadow, size, matWorldToTexture );
	Vector2DCopy( size, m_Shadows[handle].m_WorldSize );
	
	// Compute the falloff attenuation
	// Area computation isn't exact since xvec is not perp to yvec, but close enough
//	float shadowArea = size.x * size.y;	

	// The entity may be overriding our shadow cast distance
	float flShadowCastDistance = GetShadowDistance( pRenderable );
	float maxHeight = flShadowCastDistance + falloffStart; //3.0f * sqrt( shadowArea );

	CShadowLeafEnum leafList;
	BuildShadowLeafList( &leafList, worldOrigin, vecShadowDir, size, maxHeight );
	int nCount = leafList.m_LeafList.Count();
	const int *pLeafList = leafList.m_LeafList.Base();

	shadowmgr->ProjectShadow( m_Shadows[handle].m_ShadowHandle, worldOrigin,
		vecShadowDir, matWorldToTexture, size, nCount, pLeafList, maxHeight, falloffStart, MAX_FALLOFF_AMOUNT, pRenderable->GetRenderOrigin() );

	// Compute extra clip planes to prevent poke-thru
// FIXME!!!!!!!!!!!!!!  Removing this for now since it seems to mess up the blobby shadows.
//	ComputeExtraClipPlanes( pEnt, handle, vec, mins, maxs, localShadowDir );

	// Add the shadow to the client leaf system so it correctly marks 
	// leafs as being affected by a particular shadow
	ClientLeafSystem()->ProjectShadow( m_Shadows[handle].m_ClientLeafShadowHandle, nCount, pLeafList );
}

//-----------------------------------------------------------------------------
// Visualization....
//-----------------------------------------------------------------------------
void CClientShadowMgr::DrawRenderToTextureDebugInfo( IClientRenderable* pRenderable, const Vector& mins, const Vector& maxs )
{   
	// Get the object's basis
	Vector vec[3];
	AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
	vec[1] *= -1.0f;

	Vector vecSize;
	VectorSubtract( maxs, mins, vecSize );

	Vector vecOrigin = pRenderable->GetRenderOrigin();
	Vector start, end, end2;

	VectorMA( vecOrigin, mins.x, vec[0], start );
	VectorMA( start, mins.y, vec[1], start );
	VectorMA( start, mins.z, vec[2], start );

	VectorMA( start, vecSize.x, vec[0], end );
	VectorMA( end, vecSize.z, vec[2], end2 );
	debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 ); 
	debugoverlay->AddLineOverlay( end2, end, 255, 0, 0, true, 0.01 );

	VectorMA( start, vecSize.y, vec[1], end );
	VectorMA( end, vecSize.z, vec[2], end2 );
	debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 ); 
	debugoverlay->AddLineOverlay( end2, end, 255, 0, 0, true, 0.01 );

	VectorMA( start, vecSize.z, vec[2], end );
	debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );
	
	start = end;
	VectorMA( start, vecSize.x, vec[0], end );
	debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );

	VectorMA( start, vecSize.y, vec[1], end );
	debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );

	VectorMA( end, vecSize.x, vec[0], start );
	VectorMA( start, -vecSize.x, vec[0], end );
	debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );

	VectorMA( start, -vecSize.y, vec[1], end );
	debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );

	VectorMA( start, -vecSize.z, vec[2], end );
	debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );

	start = end;
	VectorMA( start, -vecSize.x, vec[0], end );
	debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );

	VectorMA( start, -vecSize.y, vec[1], end );
	debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );

	C_BaseEntity *pEnt = pRenderable->GetIClientUnknown()->GetBaseEntity();
	if ( pEnt )
	{
		debugoverlay->AddTextOverlay( vecOrigin, 0, "%d", pEnt->entindex() );
	}
	else
	{
		debugoverlay->AddTextOverlay( vecOrigin, 0, "%X", (size_t)pRenderable );
	}
}

extern ConVar cl_drawshadowtexture;
extern ConVar cl_shadowtextureoverlaysize;

//-----------------------------------------------------------------------------
// Builds a more complex shadow...
//-----------------------------------------------------------------------------
void CClientShadowMgr::BuildRenderToTextureShadow( IClientRenderable* pRenderable, 
		ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs)
{
	if ( cl_drawshadowtexture.GetInt() )
	{
		// Red wireframe bounding box around objects whose RTT shadows are being updated that frame
		DrawRenderToTextureDebugInfo( pRenderable, mins, maxs );
	}

	// Get the object's basis
	Vector vec[3];
	AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
	vec[1] *= -1.0f;

	Vector vecShadowDir = GetShadowDirection( handle );

//	Debugging aid
//	const model_t *pModel = pRenderable->GetModel();
//	const char *pDebugName = modelinfo->GetModelName( pModel );

	// Project the shadow casting direction into the space of the object
	Vector localShadowDir;
	localShadowDir[0] = DotProduct( vec[0], vecShadowDir );
	localShadowDir[1] = DotProduct( vec[1], vecShadowDir );
	localShadowDir[2] = DotProduct( vec[2], vecShadowDir );

	// Compute the box size
	Vector boxSize;
	VectorSubtract( maxs, mins, boxSize );
	
	Vector yvec;
	float fProjMax = 0.0f;
	for( int i = 0; i != 3; ++i )
	{
		Vector test = vec[i] - ( vecShadowDir * DotProduct( vecShadowDir, vec[i] ) );
		test *= boxSize[i]; //doing after the projection to simplify projection math
		float fLengthSqr = test.LengthSqr();
		if( fLengthSqr > fProjMax )
		{
			fProjMax = fLengthSqr;
			yvec = test;
		}
	}		

	VectorNormalize( yvec );

	// Compute the x vector
	Vector xvec;
	CrossProduct( yvec, vecShadowDir, xvec );

	// We project the two longest sides into the vectors perpendicular
	// to the projection direction, then add in the projection of the perp direction
	Vector2D size;
	size.x = boxSize.x * fabs( DotProduct( vec[0], xvec ) ) + 
		boxSize.y * fabs( DotProduct( vec[1], xvec ) ) + 
		boxSize.z * fabs( DotProduct( vec[2], xvec ) );
	size.y = boxSize.x * fabs( DotProduct( vec[0], yvec ) ) + 
		boxSize.y * fabs( DotProduct( vec[1], yvec ) ) + 
		boxSize.z * fabs( DotProduct( vec[2], yvec ) );

	size.x += 2.0f * TEXEL_SIZE_PER_CASTER_SIZE;
	size.y += 2.0f * TEXEL_SIZE_PER_CASTER_SIZE;

	// Place the origin at the point with min dot product with shadow dir
	Vector org;
	float falloffStart = ComputeLocalShadowOrigin( pRenderable, mins, maxs, localShadowDir, 1.0f, org );

	// Transform the local origin into world coordinates
	Vector worldOrigin = pRenderable->GetRenderOrigin( );
	VectorMA( worldOrigin, org.x, vec[0], worldOrigin );
	VectorMA( worldOrigin, org.y, vec[1], worldOrigin );
	VectorMA( worldOrigin, org.z, vec[2], worldOrigin );

	VMatrix matWorldToTexture;
	BuildOrthoWorldToShadowMatrix( m_Shadows[handle].m_WorldToShadow, worldOrigin, vecShadowDir, xvec, yvec );
	BuildWorldToTextureMatrix( m_Shadows[handle].m_WorldToShadow, size, matWorldToTexture );
	Vector2DCopy( size, m_Shadows[handle].m_WorldSize );

	// Compute the falloff attenuation
	// Area computation isn't exact since xvec is not perp to yvec, but close enough
	// Extra factor of 4 in the maxHeight due to the size being half as big
//	float shadowArea = size.x * size.y;	

	// The entity may be overriding our shadow cast distance
	float flShadowCastDistance = GetShadowDistance( pRenderable );
	float maxHeight = flShadowCastDistance + falloffStart; //3.0f * sqrt( shadowArea );

	CShadowLeafEnum leafList;
	BuildShadowLeafList( &leafList, worldOrigin, vecShadowDir, size, maxHeight );
	int nCount = leafList.m_LeafList.Count();
	const int *pLeafList = leafList.m_LeafList.Base();

	shadowmgr->ProjectShadow( m_Shadows[handle].m_ShadowHandle, worldOrigin, 
		vecShadowDir, matWorldToTexture, size, nCount, pLeafList, maxHeight, falloffStart, MAX_FALLOFF_AMOUNT, pRenderable->GetRenderOrigin() );

	// Compute extra clip planes to prevent poke-thru
	ComputeExtraClipPlanes( pRenderable, handle, vec, mins, maxs, localShadowDir );

	// Add the shadow to the client leaf system so it correctly marks 
	// leafs as being affected by a particular shadow
	ClientLeafSystem()->ProjectShadow( m_Shadows[handle].m_ClientLeafShadowHandle, nCount, pLeafList );
}

static void LineDrawHelper( const Vector &startShadowSpace, const Vector &endShadowSpace, 
						   const VMatrix &shadowToWorld, unsigned char r = 255, unsigned char g = 255, 
						   unsigned char b = 255 )
{
	Vector startWorldSpace, endWorldSpace;
	Vector3DMultiplyPositionProjective( shadowToWorld, startShadowSpace, startWorldSpace );
	Vector3DMultiplyPositionProjective( shadowToWorld, endShadowSpace, endWorldSpace );

	debugoverlay->AddLineOverlay( startWorldSpace + Vector( 0.0f, 0.0f, 1.0f ), 
		endWorldSpace + Vector( 0.0f, 0.0f, 1.0f ), r, g, b, false, -1 );
}

static void DebugDrawFrustum( const Vector &vOrigin, const VMatrix &matWorldToFlashlight )
{
	VMatrix flashlightToWorld;
	MatrixInverseGeneral( matWorldToFlashlight, flashlightToWorld );
	
	// Draw boundaries of frustum
	LineDrawHelper( Vector( 0.0f, 0.0f, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
	LineDrawHelper( Vector( 0.0f, 0.0f, 1.0f ), Vector( 0.0f, 1.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
	LineDrawHelper( Vector( 0.0f, 1.0f, 1.0f ), Vector( 0.0f, 1.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
	LineDrawHelper( Vector( 0.0f, 1.0f, 0.0f ), Vector( 0.0f, 0.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
	LineDrawHelper( Vector( 1.0f, 0.0f, 0.0f ), Vector( 1.0f, 0.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
	LineDrawHelper( Vector( 1.0f, 0.0f, 1.0f ), Vector( 1.0f, 1.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
	LineDrawHelper( Vector( 1.0f, 1.0f, 1.0f ), Vector( 1.0f, 1.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
	LineDrawHelper( Vector( 1.0f, 1.0f, 0.0f ), Vector( 1.0f, 0.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
	LineDrawHelper( Vector( 0.0f, 0.0f, 0.0f ), Vector( 1.0f, 0.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );
	LineDrawHelper( Vector( 0.0f, 0.0f, 1.0f ), Vector( 1.0f, 0.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
	LineDrawHelper( Vector( 0.0f, 1.0f, 1.0f ), Vector( 1.0f, 1.0f, 1.0f ), flashlightToWorld, 255, 255, 255 );
	LineDrawHelper( Vector( 0.0f, 1.0f, 0.0f ), Vector( 1.0f, 1.0f, 0.0f ), flashlightToWorld, 255, 255, 255 );

	// Draw RGB triad at front plane
	LineDrawHelper( Vector( 0.5f, 0.5f, 0.0f ), Vector( 1.0f, 0.5f, 0.0f ),  flashlightToWorld, 255,   0,   0 );
	LineDrawHelper( Vector( 0.5f, 0.5f, 0.0f ), Vector( 0.5f, 1.0f, 0.0f ),  flashlightToWorld,   0, 255,   0 );
	LineDrawHelper( Vector( 0.5f, 0.5f, 0.0f ), Vector( 0.5f, 0.5f, 0.35f ), flashlightToWorld,   0,   0, 255 );
}

//-----------------------------------------------------------------------------
// Builds a list of leaves inside the flashlight volume
//-----------------------------------------------------------------------------
static void BuildFlashlightLeafList( CShadowLeafEnum *pEnum, const VMatrix &worldToShadow )
{
	// Use an AABB around the frustum to enumerate leaves.
	Vector mins, maxs;
	CalculateAABBFromProjectionMatrix( worldToShadow, &mins, &maxs );
	ISpatialQuery* pQuery = engine->GetBSPTreeQuery();
	pQuery->EnumerateLeavesInBox( mins, maxs, pEnum, 0 );
}

void CClientShadowMgr::BuildFlashlight( ClientShadowHandle_t handle )
{
	// For the 360, we just draw flashlights with the main geometry
	// and bypass the entire shadow casting system.
	ClientShadow_t &shadow = m_Shadows[handle];
	if ( IsX360() || r_flashlight_version2.GetInt() )
	{
		// This will update the matrices, but not do work to add the flashlight to surfaces
		shadowmgr->ProjectFlashlight( shadow.m_ShadowHandle, shadow.m_WorldToShadow, 0, NULL );
		return;
	}

	VPROF_BUDGET( "CClientShadowMgr::BuildFlashlight", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );

	bool bLightModels = r_flashlightmodels.GetBool();
	bool bLightSpecificEntity = shadow.m_hTargetEntity.Get() != NULL;
	bool bLightWorld = ( shadow.m_Flags & SHADOW_FLAGS_LIGHT_WORLD ) != 0;
	int nCount = 0;
	const int *pLeafList = 0;

	CShadowLeafEnum leafList;
	if ( bLightWorld || ( bLightModels && !bLightSpecificEntity ) )
	{
		BuildFlashlightLeafList( &leafList, shadow.m_WorldToShadow );
		nCount = leafList.m_LeafList.Count();
		pLeafList = leafList.m_LeafList.Base();
	}

	if( bLightWorld )
	{
		shadowmgr->ProjectFlashlight( shadow.m_ShadowHandle, shadow.m_WorldToShadow, nCount, pLeafList );
	}
	else
	{
		// This should clear all models and surfaces from this shadow
		shadowmgr->EnableShadow( shadow.m_ShadowHandle, false );
		shadowmgr->EnableShadow( shadow.m_ShadowHandle, true );
	}

	if ( !bLightModels )
		return;

	if ( !bLightSpecificEntity )
	{
		// Add the shadow to the client leaf system so it correctly marks 
		// leafs as being affected by a particular shadow
		ClientLeafSystem()->ProjectFlashlight( shadow.m_ClientLeafShadowHandle, nCount, pLeafList );
		return;
	}

	// We know what we are focused on, so just add the shadow directly to that receiver
	Assert( shadow.m_hTargetEntity->GetModel() );

	C_BaseEntity *pChild = shadow.m_hTargetEntity->FirstMoveChild();
	while( pChild )
	{
		int modelType = modelinfo->GetModelType( pChild->GetModel() );
		if (modelType == mod_brush)
		{
			AddShadowToReceiver( handle, pChild, SHADOW_RECEIVER_BRUSH_MODEL );
		}
		else if ( modelType == mod_studio )
		{
			AddShadowToReceiver( handle, pChild, SHADOW_RECEIVER_STUDIO_MODEL );
		}

		pChild = pChild->NextMovePeer();
	}

	int modelType = modelinfo->GetModelType( shadow.m_hTargetEntity->GetModel() );
	if (modelType == mod_brush)
	{
		AddShadowToReceiver( handle, shadow.m_hTargetEntity, SHADOW_RECEIVER_BRUSH_MODEL );
	}
	else if ( modelType == mod_studio )
	{
		AddShadowToReceiver( handle, shadow.m_hTargetEntity, SHADOW_RECEIVER_STUDIO_MODEL );
	}
}

//-----------------------------------------------------------------------------
// Adds the child bounds to the bounding box
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddChildBounds( matrix3x4_t &matWorldToBBox, IClientRenderable* pParent, Vector &vecMins, Vector &vecMaxs )
{
	Vector vecChildMins, vecChildMaxs;
	Vector vecNewChildMins, vecNewChildMaxs;
	matrix3x4_t childToBBox;

	IClientRenderable *pChild = pParent->FirstShadowChild();
	while( pChild )
	{
		// Transform the child bbox into the space of the main bbox
		// FIXME: Optimize this?
		if ( GetActualShadowCastType( pChild ) != SHADOWS_NONE)
		{
			pChild->GetShadowRenderBounds( vecChildMins, vecChildMaxs, SHADOWS_RENDER_TO_TEXTURE );
			ConcatTransforms( matWorldToBBox, pChild->RenderableToWorldTransform(), childToBBox );
			TransformAABB( childToBBox, vecChildMins, vecChildMaxs, vecNewChildMins, vecNewChildMaxs );
			VectorMin( vecMins, vecNewChildMins, vecMins );
			VectorMax( vecMaxs, vecNewChildMaxs, vecMaxs );
		}

		AddChildBounds( matWorldToBBox, pChild, vecMins, vecMaxs );
		pChild = pChild->NextShadowPeer();
	}
}

//-----------------------------------------------------------------------------
// Compute a bounds for the entity + children
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeHierarchicalBounds( IClientRenderable *pRenderable, Vector &vecMins, Vector &vecMaxs )
{
	ShadowType_t shadowType = GetActualShadowCastType( pRenderable );

	pRenderable->GetShadowRenderBounds( vecMins, vecMaxs, shadowType );

	// We could use a good solution for this in the regular PC build, since
	// it causes lots of extra bone setups for entities you can't see.
	if ( IsPC() )
	{
		IClientRenderable *pChild = pRenderable->FirstShadowChild();

		// Don't recurse down the tree when we hit a blobby shadow
		if ( pChild && shadowType != SHADOWS_SIMPLE )
		{
			matrix3x4_t matWorldToBBox;
			MatrixInvert( pRenderable->RenderableToWorldTransform(), matWorldToBBox );
			AddChildBounds( matWorldToBBox, pRenderable, vecMins, vecMaxs );
		}
	}
}

//-----------------------------------------------------------------------------
// Shadow update functions
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateStudioShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle )
{
	if( !( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) )
	{
		Vector mins, maxs;
		ComputeHierarchicalBounds( pRenderable, mins, maxs );

		ShadowType_t shadowType = GetActualShadowCastType( handle );
		if ( shadowType != SHADOWS_RENDER_TO_TEXTURE )
		{
			BuildOrthoShadow( pRenderable, handle, mins, maxs );
		}
		else
		{
			BuildRenderToTextureShadow( pRenderable, handle, mins, maxs );
		}
	}
	else
	{
		BuildFlashlight( handle );
	}
}

void CClientShadowMgr::UpdateBrushShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle )
{
	if( !( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) )
	{
		// Compute the bounding box in the space of the shadow...
		Vector mins, maxs;
		ComputeHierarchicalBounds( pRenderable, mins, maxs );

		ShadowType_t shadowType = GetActualShadowCastType( handle );
		if ( shadowType != SHADOWS_RENDER_TO_TEXTURE )
		{
			BuildOrthoShadow( pRenderable, handle, mins, maxs );
		}
		else
		{
			BuildRenderToTextureShadow( pRenderable, handle, mins, maxs );
		}
	}
	else
	{
		VPROF_BUDGET( "CClientShadowMgr::UpdateBrushShadow", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );

		BuildFlashlight( handle );
	}
}

#ifdef _DEBUG

static bool s_bBreak = false;

void ShadowBreak_f()
{
	s_bBreak = true;
}

static ConCommand r_shadowbreak("r_shadowbreak", ShadowBreak_f);

#endif // _DEBUG

bool CClientShadowMgr::WillParentRenderBlobbyShadow( IClientRenderable *pRenderable )
{
	if ( !pRenderable )
		return false;

	IClientRenderable *pShadowParent = pRenderable->GetShadowParent();
	if ( !pShadowParent )
		return false;

	// If there's *no* shadow casting type, then we want to see if we can render into its parent
 	ShadowType_t shadowType = GetActualShadowCastType( pShadowParent );
	if ( shadowType == SHADOWS_NONE )
		return WillParentRenderBlobbyShadow( pShadowParent );

	return shadowType == SHADOWS_SIMPLE;
}

//-----------------------------------------------------------------------------
// Are we the child of a shadow with render-to-texture?
//-----------------------------------------------------------------------------
bool CClientShadowMgr::ShouldUseParentShadow( IClientRenderable *pRenderable )
{
	if ( !pRenderable )
		return false;

	IClientRenderable *pShadowParent = pRenderable->GetShadowParent();
	if ( !pShadowParent )
		return false;

	// Can't render into the parent if the parent is blobby
	ShadowType_t shadowType = GetActualShadowCastType( pShadowParent );
	if ( shadowType == SHADOWS_SIMPLE )
		return false;

	// If there's *no* shadow casting type, then we want to see if we can render into its parent
 	if ( shadowType == SHADOWS_NONE )
		return ShouldUseParentShadow( pShadowParent );

	// Here, the parent uses a render-to-texture shadow
	return true;
}

//-----------------------------------------------------------------------------
// Before we render any view, make sure all shadows are re-projected vs world
//-----------------------------------------------------------------------------
void CClientShadowMgr::PreRender()
{
	VPROF_BUDGET( "CClientShadowMgr::PreRender", VPROF_BUDGETGROUP_SHADOW_RENDERING );
	MDLCACHE_CRITICAL_SECTION();

	//
	// -- Shadow Depth Textures -----------------------
	//

	{
		// VPROF scope
		VPROF_BUDGET( "CClientShadowMgr::PreRender", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );

		// If someone turned shadow depth mapping on but we can't do it, force it off
		if ( r_flashlightdepthtexture.GetBool() && !materials->SupportsShadowDepthTextures() )
		{
			r_flashlightdepthtexture.SetValue( 0 );
			ShutdownDepthTextureShadows();	
		}

		bool bDepthTextureActive     = r_flashlightdepthtexture.GetBool();
		int  nDepthTextureResolution = r_flashlightdepthres.GetInt();

		// If shadow depth texture size or enable/disable changed, do appropriate deallocation/(re)allocation
		if ( ( bDepthTextureActive != m_bDepthTextureActive ) || ( nDepthTextureResolution != m_nDepthTextureResolution ) )
		{
			// If shadow depth texturing remains on, but resolution changed, shut down and reinitialize depth textures
			if ( ( bDepthTextureActive == true ) && ( m_bDepthTextureActive == true ) &&
				 ( nDepthTextureResolution != m_nDepthTextureResolution ) )
			{
				ShutdownDepthTextureShadows();	
				InitDepthTextureShadows();
			}
			else
			{
				if ( m_bDepthTextureActive && !bDepthTextureActive )		// Turning off shadow depth texturing
				{
					ShutdownDepthTextureShadows();
				}
				else if ( bDepthTextureActive && !m_bDepthTextureActive)	// Turning on shadow depth mapping
				{
					InitDepthTextureShadows();
				}
			}
		}
	}

	//
	// -- Render to Texture Shadows -----------------------
	//

	bool bRenderToTextureActive = r_shadowrendertotexture.GetBool();
	if ( bRenderToTextureActive != m_RenderToTextureActive )
	{
		if ( m_RenderToTextureActive )
		{
			ShutdownRenderToTextureShadows();
		}
		else
		{
			InitRenderToTextureShadows();
		}

		UpdateAllShadows();
		return;
	}

	m_bUpdatingDirtyShadows = true;

	unsigned short i = m_DirtyShadows.FirstInorder();
	while ( i != m_DirtyShadows.InvalidIndex() )
	{
		MDLCACHE_CRITICAL_SECTION();
		ClientShadowHandle_t& handle = m_DirtyShadows[ i ];
		UpdateDirtyShadow( handle );
		i = m_DirtyShadows.NextInorder(i);
	}
	m_DirtyShadows.RemoveAll();

	// Transparent shadows must remain dirty, since they were not re-projected
	int nCount = m_TransparentShadows.Count();
	for ( int i = 0; i < nCount; ++i )
	{
		m_DirtyShadows.Insert( m_TransparentShadows[i] );
	}
	m_TransparentShadows.RemoveAll();

	m_bUpdatingDirtyShadows = false;
}

//-----------------------------------------------------------------------------
// Gets the entity whose shadow this shadow will render into
//-----------------------------------------------------------------------------
IClientRenderable *CClientShadowMgr::GetParentShadowEntity( ClientShadowHandle_t handle )
{
	ClientShadow_t& shadow = m_Shadows[handle];
	IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
	if ( pRenderable )
	{
		if ( ShouldUseParentShadow( pRenderable ) )
		{
			IClientRenderable *pParent = pRenderable->GetShadowParent();
			while ( GetActualShadowCastType( pParent ) == SHADOWS_NONE )
			{
				pParent = pParent->GetShadowParent();
				Assert( pParent );
			}
			return pParent;
		}
	}
	return NULL;
}

//-----------------------------------------------------------------------------
// Marks a shadow as needing re-projection
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddToDirtyShadowList( ClientShadowHandle_t handle, bool bForce )
{
	// Don't add to the dirty shadow list while we're iterating over it
	// The only way this can happen is if a child is being rendered into a parent
	// shadow, and we don't need it to be added to the dirty list in that case.
	if ( m_bUpdatingDirtyShadows )
		return;

	if ( handle == CLIENTSHADOW_INVALID_HANDLE )
		return;

	Assert( m_DirtyShadows.Find( handle ) == m_DirtyShadows.InvalidIndex() );
	m_DirtyShadows.Insert( handle );

	// This pretty much guarantees we'll recompute the shadow
	if ( bForce )
	{
		m_Shadows[handle].m_LastAngles.Init( FLT_MAX, FLT_MAX, FLT_MAX );
	}

	// If we use our parent shadow, then it's dirty too...
	IClientRenderable *pParent = GetParentShadowEntity( handle );
	if ( pParent )
	{
		AddToDirtyShadowList( pParent, bForce );
	}
}

//-----------------------------------------------------------------------------
// Marks a shadow as needing re-projection
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddToDirtyShadowList( IClientRenderable *pRenderable, bool bForce )
{
	// Don't add to the dirty shadow list while we're iterating over it
	// The only way this can happen is if a child is being rendered into a parent
	// shadow, and we don't need it to be added to the dirty list in that case.
	if ( m_bUpdatingDirtyShadows )
		return;

	// Are we already in the dirty list?
	if ( pRenderable->IsShadowDirty( ) )
		return;

	ClientShadowHandle_t handle = pRenderable->GetShadowHandle();
	if ( handle == CLIENTSHADOW_INVALID_HANDLE )
		return;

#ifdef _DEBUG
	// Make sure everything's consistent
	if ( handle != CLIENTSHADOW_INVALID_HANDLE )
	{
		IClientRenderable *pShadowRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[handle].m_Entity );
		Assert( pRenderable == pShadowRenderable );
	}
#endif

	pRenderable->MarkShadowDirty( true );
	AddToDirtyShadowList( handle, bForce );
}

//-----------------------------------------------------------------------------
// Marks the render-to-texture shadow as needing to be re-rendered
//-----------------------------------------------------------------------------
void CClientShadowMgr::MarkRenderToTextureShadowDirty( ClientShadowHandle_t handle )
{
	// Don't add bogus handles!
	if (handle != CLIENTSHADOW_INVALID_HANDLE)
	{
		// Mark the shadow has having a dirty renter-to-texture
		ClientShadow_t& shadow = m_Shadows[handle];
		shadow.m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;

		// If we use our parent shadow, then it's dirty too...
		IClientRenderable *pParent = GetParentShadowEntity( handle );
		if ( pParent )
		{
			ClientShadowHandle_t parentHandle = pParent->GetShadowHandle();
			if ( parentHandle != CLIENTSHADOW_INVALID_HANDLE )
			{
				m_Shadows[parentHandle].m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Update a shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateShadow( ClientShadowHandle_t handle, bool force )
{
	ClientShadow_t& shadow = m_Shadows[handle];

	// Get the client entity....
	IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
	if ( !pRenderable )
	{
		// Retire the shadow if the entity is gone
		DestroyShadow( handle );
		return;
	}

	// Don't bother if there's no model on the renderable
	if ( !pRenderable->GetModel() )
	{
		pRenderable->MarkShadowDirty( false );
		return;
	}

	// FIXME: NOTE! Because this is called from PreRender, the falloff bias is
	// off by a frame. We could move the code in PreRender to occur after world
	// list building is done to fix this issue.
	// Don't bother with it if the shadow is totally transparent
	const ShadowInfo_t &shadowInfo = shadowmgr->GetInfo( shadow.m_ShadowHandle );
	if ( shadowInfo.m_FalloffBias == 255 )
	{
		shadowmgr->EnableShadow( shadow.m_ShadowHandle, false );
		m_TransparentShadows.AddToTail( handle );
		return;
	}

#ifdef _DEBUG
	if (s_bBreak)
	{
		s_bBreak = false;
	}
#endif
	// Hierarchical children shouldn't be projecting shadows...
	// Check to see if it's a child of an entity with a render-to-texture shadow...
	if ( ShouldUseParentShadow( pRenderable ) || WillParentRenderBlobbyShadow( pRenderable ) )
	{
		shadowmgr->EnableShadow( shadow.m_ShadowHandle, false );
		pRenderable->MarkShadowDirty( false );
		return;
	}

	shadowmgr->EnableShadow( shadow.m_ShadowHandle, true );

	// Figure out if the shadow moved...
	// Even though we have dirty bits, some entities
	// never clear those dirty bits
	const Vector& origin = pRenderable->GetRenderOrigin();
	const QAngle& angles = pRenderable->GetRenderAngles();

	if ( force || (origin != shadow.m_LastOrigin) || (angles != shadow.m_LastAngles) || shadow.m_LightPosLerp < 1.0f )
	{
		// Store off the new pos/orientation
		VectorCopy( origin, shadow.m_LastOrigin );
		VectorCopy( angles, shadow.m_LastAngles );

		CMatRenderContextPtr pRenderContext( materials );
		const model_t *pModel = pRenderable->GetModel();
		MaterialFogMode_t fogMode = pRenderContext->GetFogMode();
		pRenderContext->FogMode( MATERIAL_FOG_NONE );
		switch( modelinfo->GetModelType( pModel ) )
		{
		case mod_brush:
			UpdateBrushShadow( pRenderable, handle );
			break;

		case mod_studio:
			UpdateStudioShadow( pRenderable, handle );
			break;

		default:
			// Shouldn't get here if not a brush or studio
			Assert(0);
			break;
		}
		pRenderContext->FogMode( fogMode );
	}

	// NOTE: We can't do this earlier because pEnt->GetRenderOrigin() can
	// provoke a recomputation of render origin, which, for aiments, can cause everything
	// to be marked as dirty. So don't clear the flag until this point.
	pRenderable->MarkShadowDirty( false );
}

//-----------------------------------------------------------------------------
// Update a shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateProjectedTextureInternal( ClientShadowHandle_t handle, bool force )
{
	ClientShadow_t& shadow = m_Shadows[handle];

	if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
	{
		VPROF_BUDGET( "CClientShadowMgr::UpdateProjectedTextureInternal", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );

		Assert( ( shadow.m_Flags & SHADOW_FLAGS_SHADOW ) == 0 );
		ClientShadow_t& shadow = m_Shadows[handle];

		shadowmgr->EnableShadow( shadow.m_ShadowHandle, true );

		// FIXME: What's the difference between brush and model shadows for light projectors? Answer: nothing.
		UpdateBrushShadow( NULL, handle );
	}
	else
	{
		Assert( shadow.m_Flags & SHADOW_FLAGS_SHADOW );
		Assert( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 );
		UpdateShadow( handle, force );
	}
}

//-----------------------------------------------------------------------------
// Update a shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateProjectedTexture( ClientShadowHandle_t handle, bool force )
{
	VPROF_BUDGET( "CClientShadowMgr::UpdateProjectedTexture", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );

	if ( handle == CLIENTSHADOW_INVALID_HANDLE )
		return;

	// NOTE: This can only work for flashlights, since UpdateProjectedTextureInternal
	// depends on the falloff offset to cull shadows.
	ClientShadow_t &shadow = m_Shadows[ handle ];
	if( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 )
	{
		Warning( "CClientShadowMgr::UpdateProjectedTexture can only be used with flashlights!\n" );
		return;
	}

	UpdateProjectedTextureInternal( handle, force );
	RemoveShadowFromDirtyList( handle );
}

	
//-----------------------------------------------------------------------------
// Computes bounding sphere
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeBoundingSphere( IClientRenderable* pRenderable, Vector& origin, float& radius )
{
	Assert( pRenderable );
	Vector mins, maxs;
	pRenderable->GetShadowRenderBounds( mins, maxs, GetActualShadowCastType( pRenderable ) );
	Vector size;
	VectorSubtract( maxs, mins, size );
	radius = size.Length() * 0.5f;

	// Compute centroid (local space)
	Vector centroid;
	VectorAdd( mins, maxs, centroid );
	centroid *= 0.5f;

	// Transform centroid into world space
	Vector vec[3];
	AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
	vec[1] *= -1.0f;

	VectorCopy( pRenderable->GetRenderOrigin(), origin );
	VectorMA( origin, centroid.x, vec[0], origin );
	VectorMA( origin, centroid.y, vec[1], origin );
	VectorMA( origin, centroid.z, vec[2], origin );
}

//-----------------------------------------------------------------------------
// Computes a rough AABB encompassing the volume of the shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeShadowBBox( IClientRenderable *pRenderable, ClientShadowHandle_t shadowHandle, const Vector &vecAbsCenter, float flRadius, Vector *pAbsMins, Vector *pAbsMaxs )
{
	// This is *really* rough. Basically we simply determine the
	// maximum shadow casting length and extrude the box by that distance

	Vector vecShadowDir = GetShadowDirection( shadowHandle );
	for (int i = 0; i < 3; ++i)
	{
		float flShadowCastDistance = GetShadowDistance( pRenderable );
		float flDist = flShadowCastDistance * vecShadowDir[i];

		if (vecShadowDir[i] < 0)
		{
			(*pAbsMins)[i] = vecAbsCenter[i] - flRadius + flDist;
			(*pAbsMaxs)[i] = vecAbsCenter[i] + flRadius;
		}
		else
		{
			(*pAbsMins)[i] = vecAbsCenter[i] - flRadius;
			(*pAbsMaxs)[i] = vecAbsCenter[i] + flRadius + flDist;
		}
	}
}

//-----------------------------------------------------------------------------
// Compute a separating axis...
//-----------------------------------------------------------------------------
bool CClientShadowMgr::ComputeSeparatingPlane( IClientRenderable* pRend1, IClientRenderable* pRend2, cplane_t* pPlane )
{
	Vector min1, max1, min2, max2;
	pRend1->GetShadowRenderBounds( min1, max1, GetActualShadowCastType( pRend1 ) );
	pRend2->GetShadowRenderBounds( min2, max2, GetActualShadowCastType( pRend2 ) );
	return ::ComputeSeparatingPlane( 
		pRend1->GetRenderOrigin(), pRend1->GetRenderAngles(), min1, max1,
		pRend2->GetRenderOrigin(), pRend2->GetRenderAngles(), min2, max2,
		3.0f, pPlane );
}

//-----------------------------------------------------------------------------
// Cull shadows based on rough bounding volumes
//-----------------------------------------------------------------------------
bool CClientShadowMgr::CullReceiver( ClientShadowHandle_t handle, IClientRenderable* pRenderable,
									IClientRenderable* pSourceRenderable )
{
	// check flags here instead and assert !pSourceRenderable
	if( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT )
	{
		VPROF_BUDGET( "CClientShadowMgr::CullReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );

		Assert( !pSourceRenderable );	
		const Frustum_t &frustum = shadowmgr->GetFlashlightFrustum( m_Shadows[handle].m_ShadowHandle );

		Vector mins, maxs;
		pRenderable->GetRenderBoundsWorldspace( mins, maxs );

		return R_CullBox( mins, maxs, frustum );
	}

	Assert( pSourceRenderable );	
	// Compute a bounding sphere for the renderable
	Vector origin;
	float radius;
	ComputeBoundingSphere( pRenderable, origin, radius );

	// Transform the sphere center into the space of the shadow
	Vector localOrigin;
	const ClientShadow_t& shadow = m_Shadows[handle];
	const ShadowInfo_t& info = shadowmgr->GetInfo( shadow.m_ShadowHandle );
	Vector3DMultiplyPosition( shadow.m_WorldToShadow, origin, localOrigin );

	// Compute a rough bounding box for the shadow (in shadow space)
	Vector shadowMin, shadowMax;
	shadowMin.Init( -shadow.m_WorldSize.x * 0.5f, -shadow.m_WorldSize.y * 0.5f, 0 );
	shadowMax.Init( shadow.m_WorldSize.x * 0.5f, shadow.m_WorldSize.y * 0.5f, info.m_MaxDist );

	// If the bounding sphere doesn't intersect with the shadow volume, cull
	if (!IsBoxIntersectingSphere( shadowMin, shadowMax, localOrigin, radius ))
		return true;

	Vector originSource;
	float radiusSource;
	ComputeBoundingSphere( pSourceRenderable, originSource, radiusSource );

	// Fast check for separating plane...
	bool foundSeparatingPlane = false;
	cplane_t plane;
	if (!IsSphereIntersectingSphere( originSource, radiusSource, origin, radius ))
	{
		foundSeparatingPlane = true;

		// the plane normal doesn't need to be normalized...
		VectorSubtract( origin, originSource, plane.normal );
	}
	else
	{
		foundSeparatingPlane = ComputeSeparatingPlane( pRenderable, pSourceRenderable, &plane );
	}

	if (foundSeparatingPlane)
	{
		// Compute which side of the plane the renderable is on..
	    Vector vecShadowDir = GetShadowDirection( handle );
		float shadowDot = DotProduct( vecShadowDir, plane.normal );
		float receiverDot = DotProduct( plane.normal, origin );
		float sourceDot = DotProduct( plane.normal, originSource );

		if (shadowDot > 0.0f)
		{
			if (receiverDot <= sourceDot)
			{
//				Vector dest;
//				VectorMA( pSourceRenderable->GetRenderOrigin(), 50, plane.normal, dest ); 
//				debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 255, 0, true, 1.0f );
				return true;
			}
			else
			{
//				Vector dest;
//				VectorMA( pSourceRenderable->GetRenderOrigin(), 50, plane.normal, dest ); 
//				debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 0, 0, true, 1.0f );
			}
		}
		else
		{
			if (receiverDot >= sourceDot)
			{
//				Vector dest;
//				VectorMA( pSourceRenderable->GetRenderOrigin(), -50, plane.normal, dest ); 
//				debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 255, 0, true, 1.0f );
				return true;
			}
			else
			{
//				Vector dest;
//				VectorMA( pSourceRenderable->GetRenderOrigin(), 50, plane.normal, dest ); 
//				debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 0, 0, true, 1.0f );
			}
		}
	}

	// No additional clip planes? ok then it's a valid receiver
	/*
	if (shadow.m_ClipPlaneCount == 0)
		return false;

	// Check the additional cull planes
	int i;
	for ( i = 0; i < shadow.m_ClipPlaneCount; ++i)
	{
		// Fast sphere cull
		if (DotProduct( origin, shadow.m_ClipPlane[i] ) - radius > shadow.m_ClipDist[i])
			return true;
	}

	// More expensive box on plane side cull...
	Vector vec[3];
	Vector mins, maxs;
	cplane_t plane;
	AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
	pRenderable->GetBounds( mins, maxs );

	for ( i = 0; i < shadow.m_ClipPlaneCount; ++i)
	{
		// Transform the plane into the space of the receiver
		plane.normal.x = DotProduct( vec[0], shadow.m_ClipPlane[i] );
		plane.normal.y = DotProduct( vec[1], shadow.m_ClipPlane[i] );
		plane.normal.z = DotProduct( vec[2], shadow.m_ClipPlane[i] );

		plane.dist = shadow.m_ClipDist[i] - DotProduct( shadow.m_ClipPlane[i], pRenderable->GetRenderOrigin() );

		// If the box is on the front side of the plane, we're done.
		if (BoxOnPlaneSide2( mins, maxs, &plane, 3.0f ) == 1)
			return true;
	}
	*/

	return false;
}

//-----------------------------------------------------------------------------
// deals with shadows being added to shadow receivers
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddShadowToReceiver( ClientShadowHandle_t handle,
	IClientRenderable* pRenderable, ShadowReceiver_t type )
{
	ClientShadow_t &shadow = m_Shadows[handle];

	// Don't add a shadow cast by an object to itself...
	IClientRenderable* pSourceRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );

	// NOTE: if pSourceRenderable == NULL, the source is probably a flashlight since there is no entity.
	if (pSourceRenderable == pRenderable)
		return;

	// Don't bother if this renderable doesn't receive shadows or light from flashlights
	if( !pRenderable->ShouldReceiveProjectedTextures( SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ) )
		return;

	// Cull if the origin is on the wrong side of a shadow clip plane....
	if ( CullReceiver( handle, pRenderable, pSourceRenderable ) )
		return;

	// Do different things depending on the receiver type
	switch( type )
	{
	case SHADOW_RECEIVER_BRUSH_MODEL:

		if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
		{
			VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );

			if( (!shadow.m_hTargetEntity) || IsFlashlightTarget( handle, pRenderable ) )
			{
				shadowmgr->AddShadowToBrushModel( shadow.m_ShadowHandle, 
					const_cast<model_t*>(pRenderable->GetModel()),
					pRenderable->GetRenderOrigin(), pRenderable->GetRenderAngles() );

				shadowmgr->AddFlashlightRenderable( shadow.m_ShadowHandle, pRenderable );
			}
		}
		else
		{
			shadowmgr->AddShadowToBrushModel( shadow.m_ShadowHandle, 
				const_cast<model_t*>(pRenderable->GetModel()),
				pRenderable->GetRenderOrigin(), pRenderable->GetRenderAngles() );
		}
		break;

	case SHADOW_RECEIVER_STATIC_PROP:
		// Don't add shadows to props if we're not using render-to-texture
		if ( GetActualShadowCastType( handle ) == SHADOWS_RENDER_TO_TEXTURE )
		{
			// Also don't add them unless an NPC or player casts them..
			// They are wickedly expensive!!!
			C_BaseEntity *pEnt = pSourceRenderable->GetIClientUnknown()->GetBaseEntity();
			if ( pEnt && ( pEnt->GetFlags() & (FL_NPC | FL_CLIENT)) )
			{
				staticpropmgr->AddShadowToStaticProp( shadow.m_ShadowHandle, pRenderable );
			}
		}
		else if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
		{
			VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );

			if( (!shadow.m_hTargetEntity) || IsFlashlightTarget( handle, pRenderable ) )
			{
				staticpropmgr->AddShadowToStaticProp( shadow.m_ShadowHandle, pRenderable );

				shadowmgr->AddFlashlightRenderable( shadow.m_ShadowHandle, pRenderable );
			}
		}
		break;

	case SHADOW_RECEIVER_STUDIO_MODEL:
		if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
		{
			VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );

			if( (!shadow.m_hTargetEntity) || IsFlashlightTarget( handle, pRenderable ) )
			{
				pRenderable->CreateModelInstance();
				shadowmgr->AddShadowToModel( shadow.m_ShadowHandle, pRenderable->GetModelInstance() );
				shadowmgr->AddFlashlightRenderable( shadow.m_ShadowHandle, pRenderable );
			}
		}
		break;
//	default:
	}
}

//-----------------------------------------------------------------------------
// deals with shadows being added to shadow receivers
//-----------------------------------------------------------------------------
void CClientShadowMgr::RemoveAllShadowsFromReceiver( 
					IClientRenderable* pRenderable, ShadowReceiver_t type )
{
	// Don't bother if this renderable doesn't receive shadows
	if ( !pRenderable->ShouldReceiveProjectedTextures( SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ) )
		return;

	// Do different things depending on the receiver type
	switch( type )
	{
	case SHADOW_RECEIVER_BRUSH_MODEL:
		{
			model_t* pModel = const_cast<model_t*>(pRenderable->GetModel());
			shadowmgr->RemoveAllShadowsFromBrushModel( pModel );
		}
		break;

	case SHADOW_RECEIVER_STATIC_PROP:
		staticpropmgr->RemoveAllShadowsFromStaticProp(pRenderable);
		break;

	case SHADOW_RECEIVER_STUDIO_MODEL:
		if( pRenderable && pRenderable->GetModelInstance() != MODEL_INSTANCE_INVALID )
		{
			shadowmgr->RemoveAllShadowsFromModel( pRenderable->GetModelInstance() );
		}
		break;

//	default:
//		// FIXME: How do deal with this stuff? Add a method to IClientRenderable?
//		C_BaseEntity* pEnt = static_cast<C_BaseEntity*>(pRenderable);
//		pEnt->RemoveAllShadows();
	}
}

//-----------------------------------------------------------------------------
// Computes + sets the render-to-texture texcoords
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetRenderToTextureShadowTexCoords( ShadowHandle_t handle, int x, int y, int w, int h )
{
	// Let the shadow mgr know about the texture coordinates...
	// That way it'll be able to batch rendering better.
	int textureW, textureH;
	m_ShadowAllocator.GetTotalTextureSize( textureW, textureH );

	// Go in a half-pixel to avoid blending with neighboring textures..
	float u, v, du, dv;

	u  = ((float)x + 0.5f) / (float)textureW;
	v  = ((float)y + 0.5f) / (float)textureH;
	du = ((float)w - 1) / (float)textureW;
	dv = ((float)h - 1) / (float)textureH;

	shadowmgr->SetShadowTexCoord( handle, u, v, du, dv );
}

//-----------------------------------------------------------------------------
// Setup all children shadows
//-----------------------------------------------------------------------------
bool CClientShadowMgr::BuildSetupShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild )
{
	bool bDrewTexture = false;

	// Stop traversing when we hit a blobby shadow
	ShadowType_t shadowType = GetActualShadowCastType( pRenderable );
	if ( pRenderable && shadowType == SHADOWS_SIMPLE )
		return false;

	if ( !pRenderable || shadowType != SHADOWS_NONE )
	{
		bool bDrawModelShadow;
		if ( !bChild )
		{
			bDrawModelShadow = ((shadow.m_Flags & SHADOW_FLAGS_BRUSH_MODEL) == 0);
		}
		else
		{
			int nModelType = modelinfo->GetModelType( pRenderable->GetModel() );
			bDrawModelShadow = nModelType == mod_studio;
		}

		if ( bDrawModelShadow )
		{
			C_BaseEntity *pEntity = pRenderable->GetIClientUnknown()->GetBaseEntity();
			if ( pEntity )
			{
				if ( pEntity->IsNPC() )
				{
					s_NPCShadowBoneSetups.AddToTail( assert_cast<C_BaseAnimating *>( pEntity ) );
				}
				else if ( pEntity->GetBaseAnimating() )
				{
					s_NonNPCShadowBoneSetups.AddToTail( assert_cast<C_BaseAnimating *>( pEntity ) );
				}

			}
			bDrewTexture = true;
		}
	}

	if ( !pRenderable )
		return bDrewTexture;

	IClientRenderable *pChild;
	for ( pChild = pRenderable->FirstShadowChild(); pChild; pChild = pChild->NextShadowPeer() )
	{
		if ( BuildSetupShadowHierarchy( pChild, shadow, true ) )
		{
			bDrewTexture = true;
		}
	}
	return bDrewTexture;
}

//-----------------------------------------------------------------------------
// Draws all children shadows into our own
//-----------------------------------------------------------------------------
bool CClientShadowMgr::DrawShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild )
{
	bool bDrewTexture = false;

	// Stop traversing when we hit a blobby shadow
	ShadowType_t shadowType = GetActualShadowCastType( pRenderable );
	if ( pRenderable && shadowType == SHADOWS_SIMPLE )
		return false;

	if ( !pRenderable || shadowType != SHADOWS_NONE )
	{
		bool bDrawModelShadow;
		bool bDrawBrushShadow;
		if ( !bChild )
		{
			bDrawModelShadow = ((shadow.m_Flags & SHADOW_FLAGS_BRUSH_MODEL) == 0);
			bDrawBrushShadow = !bDrawModelShadow;
		}
		else
		{
			int nModelType = modelinfo->GetModelType( pRenderable->GetModel() );
			bDrawModelShadow = nModelType == mod_studio;
			bDrawBrushShadow = nModelType == mod_brush;
		}
    
		if ( bDrawModelShadow )
		{
			DrawModelInfo_t info;
			matrix3x4_t *pBoneToWorld = modelrender->DrawModelShadowSetup( pRenderable, pRenderable->GetBody(), pRenderable->GetSkin(), &info );
			if ( pBoneToWorld )
			{
				modelrender->DrawModelShadow( pRenderable, info, pBoneToWorld );
			}
			bDrewTexture = true;
		}
		else if ( bDrawBrushShadow )
		{
			render->DrawBrushModelShadow( pRenderable );
			bDrewTexture = true;
		}
	}

	if ( !pRenderable )
		return bDrewTexture;

	IClientRenderable *pChild;
	for ( pChild = pRenderable->FirstShadowChild(); pChild; pChild = pChild->NextShadowPeer() )
	{
		if ( DrawShadowHierarchy( pChild, shadow, true ) )
		{
			bDrewTexture = true;
		}
	}
	return bDrewTexture;
}

//-----------------------------------------------------------------------------
// This gets called with every shadow that potentially will need to re-render
//-----------------------------------------------------------------------------
bool CClientShadowMgr::BuildSetupListForRenderToTextureShadow( unsigned short clientShadowHandle, float flArea )
{
	ClientShadow_t& shadow = m_Shadows[clientShadowHandle];
	bool bDirtyTexture = (shadow.m_Flags & SHADOW_FLAGS_TEXTURE_DIRTY) != 0;
	bool bNeedsRedraw = m_ShadowAllocator.UseTexture( shadow.m_ShadowTexture, bDirtyTexture, flArea );
	if ( bNeedsRedraw || bDirtyTexture )
	{
		shadow.m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;

		if ( !m_ShadowAllocator.HasValidTexture( shadow.m_ShadowTexture ) )
			return false;

		// shadow to be redrawn; for now, we'll always do it.
		IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );

		if ( BuildSetupShadowHierarchy( pRenderable, shadow ) )
			return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
// This gets called with every shadow that potentially will need to re-render
//-----------------------------------------------------------------------------
bool CClientShadowMgr::DrawRenderToTextureShadow( unsigned short clientShadowHandle, float flArea )
{
	ClientShadow_t& shadow = m_Shadows[clientShadowHandle];

	// If we were previously using the LOD shadow, set the material
	bool bPreviouslyUsingLODShadow = ( shadow.m_Flags & SHADOW_FLAGS_USING_LOD_SHADOW ) != 0; 
	shadow.m_Flags &= ~SHADOW_FLAGS_USING_LOD_SHADOW;
	if ( bPreviouslyUsingLODShadow )
	{
		shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_RenderShadow, m_RenderModelShadow, (void*)(uintp)clientShadowHandle );
	}

	// Mark texture as being used...
	bool bDirtyTexture = (shadow.m_Flags & SHADOW_FLAGS_TEXTURE_DIRTY) != 0;
	bool bDrewTexture = false;
	bool bNeedsRedraw = ( !m_bThreaded && m_ShadowAllocator.UseTexture( shadow.m_ShadowTexture, bDirtyTexture, flArea ) );

	if ( !m_ShadowAllocator.HasValidTexture( shadow.m_ShadowTexture ) )
	{
		DrawRenderToTextureShadowLOD( clientShadowHandle );
		return false;
	}

	if ( bNeedsRedraw || bDirtyTexture )
	{
		// shadow to be redrawn; for now, we'll always do it.
		IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );

		CMatRenderContextPtr pRenderContext( materials );
		
		// Sets the viewport state
		int x, y, w, h;
		m_ShadowAllocator.GetTextureRect( shadow.m_ShadowTexture, x, y, w, h );
		pRenderContext->Viewport( IsX360() ? 0 : x, IsX360() ? 0 : y, w, h );

		// Clear the selected viewport only (don't need to clear depth)
		pRenderContext->ClearBuffers( true, false );

		pRenderContext->MatrixMode( MATERIAL_VIEW );
		pRenderContext->LoadMatrix( shadowmgr->GetInfo( shadow.m_ShadowHandle ).m_WorldToShadow );
   
		if ( DrawShadowHierarchy( pRenderable, shadow ) )
		{
			bDrewTexture = true;
			if ( IsX360() )
			{
				// resolve render target to system memory texture
				Rect_t srcRect = { 0, 0, w, h };
				Rect_t dstRect = { x, y, w, h };
				pRenderContext->CopyRenderTargetToTextureEx( m_ShadowAllocator.GetTexture(), 0, &srcRect, &dstRect );
			}
		}
		else
		{
			// NOTE: Think the flags reset + texcoord set should only happen in DrawShadowHierarchy
			// but it's 2 days before 360 ship.. not going to change this now.
			DevMsg( "Didn't draw shadow hierarchy.. bad shadow texcoords probably going to happen..grab Brian!\n" );
		}

		// Only clear the dirty flag if the caster isn't animating
		if ( (shadow.m_Flags & SHADOW_FLAGS_ANIMATING_SOURCE) == 0 )
		{
			shadow.m_Flags &= ~SHADOW_FLAGS_TEXTURE_DIRTY;
		}

		SetRenderToTextureShadowTexCoords( shadow.m_ShadowHandle, x, y, w, h );
	}
	else if ( bPreviouslyUsingLODShadow )
	{
		// In this case, we were previously using the LOD shadow, but we didn't
		// have to reconstitute the texture. In this case, we need to reset the texcoord
		int x, y, w, h;
		m_ShadowAllocator.GetTextureRect( shadow.m_ShadowTexture, x, y, w, h );
		SetRenderToTextureShadowTexCoords( shadow.m_ShadowHandle, x, y, w, h );
	}

	return bDrewTexture;
}

//-----------------------------------------------------------------------------
// "Draws" the shadow LOD, which really means just set up the blobby shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::DrawRenderToTextureShadowLOD( unsigned short clientShadowHandle )
{
	ClientShadow_t &shadow = m_Shadows[clientShadowHandle];
	if ( (shadow.m_Flags & SHADOW_FLAGS_USING_LOD_SHADOW) == 0 )
	{
		shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_SimpleShadow, m_SimpleShadow, (void*)CLIENTSHADOW_INVALID_HANDLE );
		shadowmgr->SetShadowTexCoord( shadow.m_ShadowHandle, 0, 0, 1, 1 );
		ClearExtraClipPlanes( clientShadowHandle ); // this was ClearExtraClipPlanes( shadow.m_ShadowHandle ), fix is from Joe Demers
		shadow.m_Flags |= SHADOW_FLAGS_USING_LOD_SHADOW;
	}
}

//-----------------------------------------------------------------------------
// Advances to the next frame, 
//-----------------------------------------------------------------------------
void CClientShadowMgr::AdvanceFrame()
{
	// We're starting the next frame
	m_ShadowAllocator.AdvanceFrame();
}

//-----------------------------------------------------------------------------
// Re-render shadow depth textures that lie in the leaf list
//-----------------------------------------------------------------------------
int CClientShadowMgr::BuildActiveShadowDepthList( const CViewSetup &viewSetup, int nMaxDepthShadows, ClientShadowHandle_t *pActiveDepthShadows )
{
	int nActiveDepthShadowCount = 0;
	for ( ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
	{
		ClientShadow_t& shadow = m_Shadows[i];

		// If this is not a flashlight which should use a shadow depth texture, skip!
		if ( ( shadow.m_Flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE ) == 0 )
			continue;

		const FlashlightState_t& flashlightState = shadowmgr->GetFlashlightState( shadow.m_ShadowHandle );

		// Bail if this flashlight doesn't want shadows
		if ( !flashlightState.m_bEnableShadows )
			continue;

		// Calculate an AABB around the shadow frustum
		Vector vecAbsMins, vecAbsMaxs;
		CalculateAABBFromProjectionMatrix( shadow.m_WorldToShadow, &vecAbsMins, &vecAbsMaxs );

		Frustum_t viewFrustum;
		GeneratePerspectiveFrustum( viewSetup.origin, viewSetup.angles, viewSetup.zNear, viewSetup.zFar, viewSetup.fov, viewSetup.m_flAspectRatio, viewFrustum );

		// FIXME: Could do other sorts of culling here, such as frustum-frustum test, distance etc.
		// If it's not in the view frustum, move on
		if ( R_CullBox( vecAbsMins, vecAbsMaxs, viewFrustum ) )
		{
			shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
			continue;
		}

		if ( nActiveDepthShadowCount >= nMaxDepthShadows )
		{
			static bool s_bOverflowWarning = false;
			if ( !s_bOverflowWarning )
			{
				Warning( "Too many depth textures rendered in a single view!\n" );
				Assert( 0 );
				s_bOverflowWarning = true;
			}
			shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
			continue;
		}

		pActiveDepthShadows[nActiveDepthShadowCount++] = i;
	}
	return nActiveDepthShadowCount;
}

//-----------------------------------------------------------------------------
// Sets the view's active flashlight render state
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetViewFlashlightState( int nActiveFlashlightCount, ClientShadowHandle_t* pActiveFlashlights )
{
	// NOTE: On the 360, we render the entire scene with the flashlight state
	// set and don't render flashlights additively in the shadow mgr at a far later time
	// because the CPU costs are prohibitive
	if ( !IsX360() && !r_flashlight_version2.GetInt() )
		return;

	Assert( nActiveFlashlightCount<= 1 ); 
	if ( nActiveFlashlightCount > 0 )
	{
		Assert( ( m_Shadows[ pActiveFlashlights[0] ].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) != 0 );
		shadowmgr->SetFlashlightRenderState( pActiveFlashlights[0] );
	}
	else
	{
		shadowmgr->SetFlashlightRenderState( SHADOW_HANDLE_INVALID );
	}
}

//-----------------------------------------------------------------------------
// Re-render shadow depth textures that lie in the leaf list
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeShadowDepthTextures( const CViewSetup &viewSetup )
{
	VPROF_BUDGET( "CClientShadowMgr::ComputeShadowDepthTextures", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );

	CMatRenderContextPtr pRenderContext( materials );
	PIXEVENT( pRenderContext, "Shadow Depth Textures" );

	// Build list of active render-to-texture shadows
	ClientShadowHandle_t pActiveDepthShadows[1024];
	int nActiveDepthShadowCount = BuildActiveShadowDepthList( viewSetup, ARRAYSIZE( pActiveDepthShadows ), pActiveDepthShadows );

	// Iterate over all existing textures and allocate shadow textures
	bool bDebugFrustum = r_flashlightdrawfrustum.GetBool();
	for ( int j = 0; j < nActiveDepthShadowCount; ++j )
	{
		ClientShadow_t& shadow = m_Shadows[ pActiveDepthShadows[j] ];

		CTextureReference shadowDepthTexture;
		bool bGotShadowDepthTexture = LockShadowDepthTexture( &shadowDepthTexture );
		if ( !bGotShadowDepthTexture )
		{
			// If we don't get one, that means we have too many this frame so bind no depth texture
			static int bitchCount = 0;
			if( bitchCount < 10 )
			{
				Warning( "Too many shadow maps this frame!\n"  );
				bitchCount++;
			}

			Assert(0);
			shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
			continue;
		}

		CViewSetup shadowView;
		shadowView.m_flAspectRatio = 1.0f;
		shadowView.x = shadowView.y = 0;
		shadowView.width = shadowDepthTexture->GetActualWidth();
		shadowView.height = shadowDepthTexture->GetActualHeight();
		shadowView.m_bOrtho = false;
		shadowView.m_bDoBloomAndToneMapping = false;

		// Copy flashlight parameters
		const FlashlightState_t& flashlightState = shadowmgr->GetFlashlightState( shadow.m_ShadowHandle );
		shadowView.fov = shadowView.fovViewmodel = flashlightState.m_fHorizontalFOVDegrees;
		shadowView.origin = flashlightState.m_vecLightOrigin;
		QuaternionAngles( flashlightState.m_quatOrientation, shadowView.angles ); // Convert from Quaternion to QAngle

		shadowView.zNear = shadowView.zNearViewmodel = flashlightState.m_NearZ;
		shadowView.zFar = shadowView.zFarViewmodel = flashlightState.m_FarZ;

		// Can turn on all light frustum overlays or per light with flashlightState parameter...
		if ( bDebugFrustum || flashlightState.m_bDrawShadowFrustum )
		{
			DebugDrawFrustum( shadowView.origin, shadow.m_WorldToShadow );
		}

		// Set depth bias factors specific to this flashlight
		CMatRenderContextPtr pRenderContext( materials );
		pRenderContext->SetShadowDepthBiasFactors( flashlightState.m_flShadowSlopeScaleDepthBias, flashlightState.m_flShadowDepthBias );

		// Render to the shadow depth texture with appropriate view
		view->UpdateShadowDepthTexture( m_DummyColorTexture, shadowDepthTexture, shadowView );

		// Associate the shadow depth texture and stencil bit with the flashlight for use during scene rendering
		shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, shadowDepthTexture, 0 );
	}

	SetViewFlashlightState( nActiveDepthShadowCount, pActiveDepthShadows );
}

	
//-----------------------------------------------------------------------------
// Re-renders all shadow textures for shadow casters that lie in the leaf list
//-----------------------------------------------------------------------------
static void SetupBonesOnBaseAnimating( C_BaseAnimating *&pBaseAnimating )
{
	pBaseAnimating->SetupBones( NULL, -1, -1, gpGlobals->curtime );
}

void CClientShadowMgr::ComputeShadowTextures( const CViewSetup &view, int leafCount, LeafIndex_t* pLeafList )
{
	VPROF_BUDGET( "CClientShadowMgr::ComputeShadowTextures", VPROF_BUDGETGROUP_SHADOW_RENDERING );

	if ( !m_RenderToTextureActive || (r_shadows.GetInt() == 0) || r_shadows_gamecontrol.GetInt() == 0 )
		return;

	m_bThreaded = false;//( r_threaded_client_shadow_manager.GetBool() && g_pThreadPool->NumIdleThreads() );

	MDLCACHE_CRITICAL_SECTION();
	// First grab all shadow textures we may want to render
	int nCount = s_VisibleShadowList.FindShadows( &view, leafCount, pLeafList );
	if ( nCount == 0 )
		return;

	// FIXME: Add heuristics based on distance, etc. to futz with
	// the shadow allocator + to select blobby shadows

	CMatRenderContextPtr pRenderContext( materials );

	PIXEVENT( pRenderContext, "Render-To-Texture Shadows" );

	// Clear to white (color unused), black alpha
	pRenderContext->ClearColor4ub( 255, 255, 255, 0 );

	// No height clip mode please.
	MaterialHeightClipMode_t oldHeightClipMode = pRenderContext->GetHeightClipMode();
	pRenderContext->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE );

	// No projection matrix (orthographic mode)
	// FIXME: Make it work for projective shadows?
	pRenderContext->MatrixMode( MATERIAL_PROJECTION );
	pRenderContext->PushMatrix();
	pRenderContext->LoadIdentity();
	pRenderContext->Scale( 1, -1, 1 );
	pRenderContext->Ortho( 0, 0, 1, 1, -9999, 0 );

	pRenderContext->MatrixMode( MATERIAL_VIEW );
	pRenderContext->PushMatrix();

	pRenderContext->PushRenderTargetAndViewport( m_ShadowAllocator.GetTexture() );

	if ( !IsX360() && m_bRenderTargetNeedsClear )
	{
		// don't need to clear absent depth buffer
		pRenderContext->ClearBuffers( true, false );
		m_bRenderTargetNeedsClear = false;
	}

	int nMaxShadows = r_shadowmaxrendered.GetInt();
	int nModelsRendered = 0;
	int i;

	if ( m_bThreaded && g_pThreadPool->NumIdleThreads() )
	{
		s_NPCShadowBoneSetups.RemoveAll();
		s_NonNPCShadowBoneSetups.RemoveAll();

		for (i = 0; i < nCount; ++i)
		{
			const VisibleShadowInfo_t &info = s_VisibleShadowList.GetVisibleShadow(i);
			if ( nModelsRendered < nMaxShadows )
			{
				if ( BuildSetupListForRenderToTextureShadow( info.m_hShadow, info.m_flArea ) )
				{
					++nModelsRendered;
				}
			}
		}

		ParallelProcess( "NPCShadowBoneSetups", s_NPCShadowBoneSetups.Base(), s_NPCShadowBoneSetups.Count(), &SetupBonesOnBaseAnimating );
		ParallelProcess( "NonNPCShadowBoneSetups", s_NonNPCShadowBoneSetups.Base(), s_NonNPCShadowBoneSetups.Count(), &SetupBonesOnBaseAnimating );

		nModelsRendered = 0;
	}

	for (i = 0; i < nCount; ++i)
	{
		const VisibleShadowInfo_t &info = s_VisibleShadowList.GetVisibleShadow(i);
		if ( nModelsRendered < nMaxShadows )
		{
			if ( DrawRenderToTextureShadow( info.m_hShadow, info.m_flArea ) )
			{
				++nModelsRendered;
			}
		}
		else
		{
			DrawRenderToTextureShadowLOD( info.m_hShadow );
		}
	}

	// Render to the backbuffer again
	pRenderContext->PopRenderTargetAndViewport();

	// Restore the matrices
	pRenderContext->MatrixMode( MATERIAL_PROJECTION );
	pRenderContext->PopMatrix();

	pRenderContext->MatrixMode( MATERIAL_VIEW );
	pRenderContext->PopMatrix();

	pRenderContext->SetHeightClipMode( oldHeightClipMode );

	pRenderContext->SetHeightClipMode( oldHeightClipMode );

	// Restore the clear color
	pRenderContext->ClearColor3ub( 0, 0, 0 );
}

//-------------------------------------------------------------------------------------------------------
// Lock down the usage of a shadow depth texture...must be unlocked for use on subsequent views / frames
//-------------------------------------------------------------------------------------------------------
bool CClientShadowMgr::LockShadowDepthTexture( CTextureReference *shadowDepthTexture )
{
	for ( int i=0; i < m_DepthTextureCache.Count(); i++ )		// Search for cached shadow depth texture
	{
		if ( m_DepthTextureCacheLocks[i] == false )				// If a free one is found
		{
			*shadowDepthTexture = m_DepthTextureCache[i];
			m_DepthTextureCacheLocks[i] = true;
			return true;
		}
	}

	return false;												// Didn't find it...
}

//------------------------------------------------------------------
// Unlock shadow depth texture for use on subsequent views / frames
//------------------------------------------------------------------
void CClientShadowMgr::UnlockAllShadowDepthTextures()
{
	for (int i=0; i< m_DepthTextureCache.Count(); i++ )
	{
		m_DepthTextureCacheLocks[i] = false;
	}
	SetViewFlashlightState( 0, NULL );
}

void CClientShadowMgr::SetFlashlightTarget( ClientShadowHandle_t shadowHandle, EHANDLE targetEntity )
{
	Assert( m_Shadows.IsValidIndex( shadowHandle ) );

	CClientShadowMgr::ClientShadow_t &shadow = m_Shadows[ shadowHandle ];
	if( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 )
		return;

//	shadow.m_pTargetRenderable = pRenderable;
	shadow.m_hTargetEntity = targetEntity;
}

void CClientShadowMgr::SetFlashlightLightWorld( ClientShadowHandle_t shadowHandle, bool bLightWorld )
{
	Assert( m_Shadows.IsValidIndex( shadowHandle ) );

	ClientShadow_t &shadow = m_Shadows[ shadowHandle ];
	if( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 )
		return;

	if ( bLightWorld )
	{
		shadow.m_Flags |= SHADOW_FLAGS_LIGHT_WORLD;
	}
	else
	{
		shadow.m_Flags &= ~SHADOW_FLAGS_LIGHT_WORLD;
	}
}

bool CClientShadowMgr::IsFlashlightTarget( ClientShadowHandle_t shadowHandle, IClientRenderable *pRenderable )
{
	ClientShadow_t &shadow = m_Shadows[ shadowHandle ];

	if( shadow.m_hTargetEntity->GetClientRenderable() == pRenderable )
		return true;

	C_BaseEntity *pChild = shadow.m_hTargetEntity->FirstMoveChild();
	while( pChild )
	{
		if( pChild->GetClientRenderable()==pRenderable )
			return true;

		pChild = pChild->NextMovePeer();
	}
							
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
const Vector &CClientShadowMgr::GetShadowDirection( ClientShadowHandle_t shadowHandle ) const
{
	Assert( shadowHandle != CLIENTSHADOW_INVALID_HANDLE );

	IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[shadowHandle].m_Entity );
	Assert( pRenderable );

	if ( !IsShadowingFromWorldLights() )
	{
		return GetShadowDirection( pRenderable );
	}

	Vector &vecResult = AllocTempVector();
	vecResult = m_Shadows[shadowHandle].m_ShadowDir;

	// Allow the renderable to override the default
	pRenderable->GetShadowCastDirection( &vecResult, GetActualShadowCastType( pRenderable ) );

	return vecResult;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateShadowDirectionFromLocalLightSource( ClientShadowHandle_t shadowHandle )
{
	Assert( shadowHandle != CLIENTSHADOW_INVALID_HANDLE );

	ClientShadow_t &shadow = m_Shadows[shadowHandle];

	IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );

	// TODO: Figure out why this still gets hit
	Assert( pRenderable );
	if ( !pRenderable )
	{
		DevWarning( "%s(): Skipping shadow with invalid client renderable (shadow handle %d)\n", __FUNCTION__, shadowHandle );
		return;
	}

	Vector bbMin, bbMax;
	pRenderable->GetRenderBoundsWorldspace( bbMin, bbMax );
	Vector origin( 0.5f * ( bbMin + bbMax ) );
	origin.z = bbMin.z; // Putting origin at the bottom of the bounding box makes the shadows a little shorter
	Vector lightPos;
	Vector lightBrightness;

	if ( shadow.m_LightPosLerp >= 1.0f ) // Skip finding new light source if we're in the middle of a lerp
	{
		// Calculate minimum brightness squared
		float flMinBrightnessSqr = r_worldlight_mincastintensity.GetFloat();
		flMinBrightnessSqr *= flMinBrightnessSqr;

		if ( g_pWorldLights->GetBrightestLightSource( pRenderable->GetRenderOrigin(), lightPos, lightBrightness ) == false || lightBrightness.LengthSqr() < flMinBrightnessSqr )
		{
			// Didn't find a light source at all, use default shadow direction
			// TODO: Could switch to using blobby shadow in this case
			lightPos.Init( FLT_MAX, FLT_MAX, FLT_MAX );
		}
	}

	if ( shadow.m_LightPosLerp == FLT_MAX )	// First light pos ever, just init
	{
		shadow.m_CurrentLightPos = lightPos;
		shadow.m_TargetLightPos = lightPos;
		shadow.m_LightPosLerp = 1.0f;
	}
	else if ( shadow.m_LightPosLerp < 1.0f )
	{
		// We're in the middle of a lerp from current to target light. Finish it.
		shadow.m_LightPosLerp += gpGlobals->frametime * 1.0f / r_worldlight_lerptime.GetFloat();
		shadow.m_LightPosLerp = clamp( shadow.m_LightPosLerp, 0.0f, 1.0f );

		Vector currLightPos( shadow.m_CurrentLightPos );
		Vector targetLightPos( shadow.m_TargetLightPos );
		if ( currLightPos.x == FLT_MAX )
		{
			currLightPos = origin - 200.0f * GetShadowDirection();
		}
		if ( targetLightPos.x == FLT_MAX )
		{
			targetLightPos = origin - 200.0f * GetShadowDirection();
		}

		// Lerp light pos
		Vector v1 = origin - shadow.m_CurrentLightPos;
		v1.NormalizeInPlace();

		Vector v2 = origin - shadow.m_TargetLightPos;
		v2.NormalizeInPlace();

		// SAULUNDONE: Caused over top sweeping far too often
#if 0
		if ( v1.Dot( v2 ) < 0.0f )
		{
			// If change in shadow angle is more than 90 degrees, lerp over the renderable's top to avoid long sweeping shadows
			Vector fakeOverheadLightPos( origin.x, origin.y, origin.z + 200.0f );
			if ( shadow.m_LightPosLerp < 0.5f )
			{
				lightPos = Lerp( 2.0f * shadow.m_LightPosLerp, currLightPos, fakeOverheadLightPos );
			}
			else
			{
				lightPos = Lerp( 2.0f * shadow.m_LightPosLerp - 1.0f, fakeOverheadLightPos, targetLightPos );
			}
		}
		else
#endif
		{
			lightPos = Lerp( shadow.m_LightPosLerp, currLightPos, targetLightPos );
		}

		if ( shadow.m_LightPosLerp >= 1.0f )
		{
			shadow.m_CurrentLightPos = shadow.m_TargetLightPos;
		}
	}
	else if ( shadow.m_LightPosLerp >= 1.0f )
	{
		// Check if we have a new closest light position and start a new lerp
		float flDistSq = ( lightPos - shadow.m_CurrentLightPos ).LengthSqr();

		if ( flDistSq > 1.0f )
		{
			// Light position has changed, which means we got a new light source. Initiate a lerp
			shadow.m_TargetLightPos = lightPos;
			shadow.m_LightPosLerp = 0.0f;
		}

		lightPos = shadow.m_CurrentLightPos;
	}

	if ( lightPos.x == FLT_MAX )
	{
		lightPos = origin - 200.0f * GetShadowDirection();
	}

	Vector vecResult( origin - lightPos );
	vecResult.NormalizeInPlace();

	vecResult.z *= r_worldlight_shortenfactor.GetFloat();
	vecResult.NormalizeInPlace();

	shadow.m_ShadowDir = vecResult;

	if ( r_worldlight_debug.GetBool() )
	{
		NDebugOverlay::Line( lightPos, origin, 255, 255, 0, false, 0.0f );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateDirtyShadow( ClientShadowHandle_t handle )
{
	Assert( m_Shadows.IsValidIndex( handle ) );
 
	if ( IsShadowingFromWorldLights() )
		UpdateShadowDirectionFromLocalLightSource( handle );
 
	UpdateProjectedTextureInternal( handle, false );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void WorldLightCastShadowCallback( IConVar *pVar, const char *pszOldValue, float flOldValue )
{
	s_ClientShadowMgr.SetShadowFromWorldLightsEnabled( r_worldlight_castshadows.GetBool() );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetShadowFromWorldLightsEnabled( bool bEnabled )
{
	if ( bEnabled == IsShadowingFromWorldLights() )
		return;
 
	m_bShadowFromWorldLights = bEnabled;
	UpdateAllShadows();
}

//-----------------------------------------------------------------------------
// A material proxy that resets the base texture to use the rendered shadow
//-----------------------------------------------------------------------------
class CShadowProxy : public IMaterialProxy
{
public:
	CShadowProxy();
	virtual ~CShadowProxy();
	virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	virtual void OnBind( void *pProxyData );
	virtual void Release( void ) { delete this; }
	virtual IMaterial *GetMaterial();

private:
	IMaterialVar* m_BaseTextureVar;
};

CShadowProxy::CShadowProxy()
{
	m_BaseTextureVar = NULL;
}

CShadowProxy::~CShadowProxy()
{
}

bool CShadowProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	bool foundVar;
	m_BaseTextureVar = pMaterial->FindVar( "$basetexture", &foundVar, false );
	return foundVar;
}

void CShadowProxy::OnBind( void *pProxyData )
{
	unsigned short clientShadowHandle = ( unsigned short )(int)pProxyData&0xffff;
	ITexture* pTex = s_ClientShadowMgr.GetShadowTexture( clientShadowHandle );
	m_BaseTextureVar->SetTextureValue( pTex );
	if ( ToolsEnabled() )
	{
		ToolFramework_RecordMaterialParams( GetMaterial() );
	}
}

IMaterial *CShadowProxy::GetMaterial()
{
	return m_BaseTextureVar->GetOwningMaterial();
}

EXPOSE_INTERFACE( CShadowProxy, IMaterialProxy, "Shadow" IMATERIAL_PROXY_INTERFACE_VERSION );

//-----------------------------------------------------------------------------
// A material proxy that resets the base texture to use the rendered shadow
//-----------------------------------------------------------------------------
class CShadowModelProxy : public IMaterialProxy
{
public:
	CShadowModelProxy();
	virtual ~CShadowModelProxy();
	virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
	virtual void OnBind( void *pProxyData );
	virtual void Release( void ) { delete this; }
	virtual IMaterial *GetMaterial();

private:
	IMaterialVar* m_BaseTextureVar;
	IMaterialVar* m_BaseTextureOffsetVar;
	IMaterialVar* m_BaseTextureScaleVar;
	IMaterialVar* m_BaseTextureMatrixVar;
	IMaterialVar* m_FalloffOffsetVar;
	IMaterialVar* m_FalloffDistanceVar;
	IMaterialVar* m_FalloffAmountVar;
};

CShadowModelProxy::CShadowModelProxy()
{
	m_BaseTextureVar = NULL;
	m_BaseTextureOffsetVar = NULL;
	m_BaseTextureScaleVar = NULL;
	m_BaseTextureMatrixVar = NULL;
	m_FalloffOffsetVar = NULL;
	m_FalloffDistanceVar = NULL;
	m_FalloffAmountVar = NULL;
}

CShadowModelProxy::~CShadowModelProxy()
{
}

bool CShadowModelProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	bool foundVar;
	m_BaseTextureVar = pMaterial->FindVar( "$basetexture", &foundVar, false );
	if (!foundVar)
		return false;
	m_BaseTextureOffsetVar = pMaterial->FindVar( "$basetextureoffset", &foundVar, false );
	if (!foundVar)
		return false;
	m_BaseTextureScaleVar = pMaterial->FindVar( "$basetexturescale", &foundVar, false );
	if (!foundVar)
		return false;
	m_BaseTextureMatrixVar = pMaterial->FindVar( "$basetexturetransform", &foundVar, false );
	if (!foundVar)
		return false;
	m_FalloffOffsetVar = pMaterial->FindVar( "$falloffoffset", &foundVar, false );
	if (!foundVar)
		return false;
	m_FalloffDistanceVar = pMaterial->FindVar( "$falloffdistance", &foundVar, false );
	if (!foundVar)
		return false;
	m_FalloffAmountVar = pMaterial->FindVar( "$falloffamount", &foundVar, false );
	return foundVar;
}

void CShadowModelProxy::OnBind( void *pProxyData )
{
	unsigned short clientShadowHandle = ( unsigned short )((int)pProxyData&0xffff);
	ITexture* pTex = s_ClientShadowMgr.GetShadowTexture( clientShadowHandle );
	m_BaseTextureVar->SetTextureValue( pTex );

	const ShadowInfo_t& info = s_ClientShadowMgr.GetShadowInfo( clientShadowHandle );
	m_BaseTextureMatrixVar->SetMatrixValue( info.m_WorldToShadow );
	m_BaseTextureOffsetVar->SetVecValue( info.m_TexOrigin.Base(), 2 );
	m_BaseTextureScaleVar->SetVecValue( info.m_TexSize.Base(), 2 );
	m_FalloffOffsetVar->SetFloatValue( info.m_FalloffOffset );
	m_FalloffDistanceVar->SetFloatValue( info.m_MaxDist );
	m_FalloffAmountVar->SetFloatValue( info.m_FalloffAmount );

	if ( ToolsEnabled() )
	{
		ToolFramework_RecordMaterialParams( GetMaterial() );
	}
}

IMaterial *CShadowModelProxy::GetMaterial()
{
	return m_BaseTextureVar->GetOwningMaterial();
}

EXPOSE_INTERFACE( CShadowModelProxy, IMaterialProxy, "ShadowModel" IMATERIAL_PROXY_INTERFACE_VERSION );
Wikipedia - Letter.png
This article has not been added to any content Wikipedia icon categories. Please help out by Wikipedia icon adding categories.
January 2024