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
Line 1: Line 1:
<source lang="cpp">
<source lang=cpp>
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//========= Copyright Valve Corporation, All rights reserved. ============//
//
//
// Purpose:  
// Purpose:  
Line 58: Line 58:


#include "cbase.h"
#include "cbase.h"
#include "engine/IShadowMgr.h"
#include "engine/ishadowmgr.h"
#include "model_types.h"
#include "model_types.h"
#include "bitmap/imageformat.h"
#include "bitmap/imageformat.h"
#include "materialsystem/IMaterialProxy.h"
#include "materialsystem/imaterialproxy.h"
#include "materialsystem/IMaterialVar.h"
#include "materialsystem/imaterialvar.h"
#include "materialsystem/IMaterial.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/IMesh.h"
#include "materialsystem/imesh.h"
#include "materialsystem/ITexture.h"
#include "materialsystem/itexture.h"
#include "BSPTreeData.h"
#include "bsptreedata.h"
#include "utlmultilist.h"
#include "utlmultilist.h"
#include "CollisionUtils.h"
#include "collisionutils.h"
#include "iviewrender.h"
#include "iviewrender.h"
#include "IVRenderView.h"
#include "ivrenderview.h"
#include "tier0/vprof.h"
#include "tier0/vprof.h"
#include "engine/ivmodelinfo.h"
#include "engine/ivmodelinfo.h"
#include "view_shared.h"
#include "view_shared.h"
#include "engine/IVDebugOverlay.h"
#include "engine/ivdebugoverlay.h"
#include "engine/IStaticPropMgr.h"
#include "engine/IStaticPropMgr.h"
#include "datacache/imdlcache.h"
#include "datacache/imdlcache.h"
#include "viewrender.h"
#include "viewrender.h"
#include "tier0/ICommandLine.h"
#include "tier0/icommandline.h"
#include "vstdlib/jobthread.h"
#include "vstdlib/jobthread.h"
#include "toolframework_client.h"
#include "toolframework_client.h"
Line 85: Line 85:
#include "debugoverlay_shared.h"
#include "debugoverlay_shared.h"
#include "worldlight.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;


// memdbgon must be the last include file in a .cpp file!!!
//-----------------------------------------------------------------------------
#include "tier0/memdbgon.h"
// Purpose: Calculate intensity ratio for a worldlight by distance
//-----------------------------------------------------------------------------
static float Engine_WorldLightDistanceFalloff( const dworldlight_t *wl, const Vector &delta )
{
float falloff;
 
switch ( wl->type )
{
case emit_surface:
// Cull out stuff that's too far
if ( wl->radius != 0 )
{
if ( DotProduct( delta, delta ) > ( wl->radius * wl->radius ) )
return 0.0f;
}
 
return InvRSquared( delta );
 
case emit_skylight:
return 1.0f;
 
case emit_quakelight:
// X - r;
falloff = wl->linear_attn - FastSqrt( DotProduct( delta, delta ) );
if ( falloff < 0 )
return 0.0f;


static ConVar r_flashlightdrawfrustum( "r_flashlightdrawfrustum", "0" );
return falloff;
static ConVar r_flashlightmodels( "r_flashlightmodels", "1" );
static ConVar r_shadowrendertotexture( "r_shadowrendertotexture", "0" );
static ConVar r_flashlight_version2( "r_flashlight_version2", "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 );
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" );
case emit_skyambient:
return 1.0f;


#if defined( _X360 )
case emit_point:
ConVar r_flashlightdepthres( "r_flashlightdepthres", "512" );
case emit_spotlight: // Directional & positional
#else
{
ConVar r_flashlightdepthres( "r_flashlightdepthres", "1024" );
float dist2, dist;
#endif


ConVar r_threaded_client_shadow_manager( "r_threaded_client_shadow_manager", "0" );
dist2 = DotProduct( delta, delta );
dist = FastSqrt( dist2 );


#ifdef _WIN32
// Cull out stuff that's too far
#pragma warning( disable: 4701 )
if ( wl->radius != 0 && dist > wl->radius )
#endif
return 0.0f;


// forward declarations
return 1.0f / ( wl->constant_attn + wl->linear_attn * dist + wl->quadratic_attn * dist2 );
void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );
}
}


return 1.0f;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// A texture allocator used to batch textures together
// Purpose: Initialize game system and members
// 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;
CWorldLights::CWorldLights() : CAutoGameSystem( "World lights" )
enum
{
{
INVALID_TEXTURE_HANDLE = (TextureHandle_t)~0
m_nWorldLights = 0;
};
m_pWorldLights = nullptr;
}


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


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


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


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


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


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


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


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


void DebugPrintCache( void );
// Grab the light lump and seek to it
lump_t &lightLump = hdr.lumps[LUMP_WORLDLIGHTS];


private:
// If the worldlights lump is empty, that means theres no normal, LDR lights to extract
typedef unsigned short FragmentHandle_t;
// This can happen when, for example, the map is compiled in HDR mode only
// So move on to the HDR worldlights lump
if ( lightLump.filelen == 0 )
{
lightLump = hdr.lumps[LUMP_WORLDLIGHTS_HDR];
}


enum
// If we can't divide the lump data into a whole number of worldlights,
// then the BSP format changed and we're unaware
if ( lightLump.filelen % sizeof( dworldlight_t ) )
{
{
INVALID_FRAGMENT_HANDLE = (FragmentHandle_t)~0,
Warning( "CWorldLights: Unknown world light lump\n" );
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
// Close file
{
g_pFullFileSystem->Close( hFile );
FragmentHandle_t m_Fragment;
return;
unsigned short m_Size;
}
unsigned short m_Power;
};


struct FragmentInfo_t
g_pFullFileSystem->Seek( hFile, lightLump.fileofs, FILESYSTEM_SEEK_HEAD );
{
unsigned short m_Block;
unsigned short m_Index;
TextureHandle_t m_Texture;


// Makes sure we don't overflow
// Allocate memory for the worldlights
unsigned int m_FrameUsed;
m_nWorldLights = lightLump.filelen / sizeof( dworldlight_t );
};
m_pWorldLights = new dworldlight_t[m_nWorldLights];


struct BlockInfo_t
// Read worldlights then close
{
g_pFullFileSystem->Read( m_pWorldLights, lightLump.filelen, hFile );
unsigned short m_FragmentPower;
g_pFullFileSystem->Close( hFile );
};


struct Cache_t
DevMsg( "CWorldLights: Load successful (%d lights at 0x%p)\n", m_nWorldLights, m_pWorldLights );
{
}
unsigned short m_List;
};


// Adds a block worth of fragments to the LRU
//-----------------------------------------------------------------------------
void AddBlockToLRU( int block );
// 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 );


// Unlink fragment from cache
// Get the PVS at our position
void UnlinkFragmentFromCache( Cache_t& cache, FragmentHandle_t fragment );
byte *pvs = new byte[nPVSSize];
g_pEngineServer->GetPVSForCluster( nCluster, nPVSSize, pvs );


// Mark something as being used (MRU)..
// Iterate through all the worldlights
void MarkUsed( FragmentHandle_t fragment );
for ( int i = 0; i < m_nWorldLights; ++i )
{
dworldlight_t *light = &m_pWorldLights[i];


// Mark something as being unused (LRU)..
// Skip skyambient
void MarkUnused( FragmentHandle_t fragment );
if ( light->type == emit_skyambient )
{
//engine->Con_NPrintf( i, "%d: skyambient", i );
continue;
}


// Disconnect texture from fragment
// Handle sun
void DisconnectTextureFromFragment( FragmentHandle_t f );
if ( light->type == emit_skylight )
{
// Calculate sun position
Vector vecAbsStart = vecPosition + Vector( 0, 0, 30 );
Vector vecAbsEnd = vecAbsStart - ( light->normal * MAX_TRACE_LENGTH );


// Returns the size of a particular fragment
trace_t tr;
int GetFragmentPower( FragmentHandle_t f ) const;
UTIL_TraceLine( vecPosition, vecAbsEnd, MASK_OPAQUE, nullptr, COLLISION_GROUP_NONE, &tr );


// Stores the actual texture we're writing into
// If we didn't hit anything then we have a problem
CTextureReference m_TexturePage;
if ( !tr.DidHit() )
{
//engine->Con_NPrintf( i, "%d: skylight: couldn't touch sky", i );
continue;
}


CUtlLinkedList< TextureInfo_t, TextureHandle_t > m_Textures;
// If we did hit something, and it wasn't the skybox, then skip
CUtlMultiList< FragmentInfo_t, FragmentHandle_t > m_Fragments;
// this worldlight
if ( !( tr.surface.flags & SURF_SKY ) && !( tr.surface.flags & SURF_SKY2D ) )
{
//engine->Con_NPrintf( i, "%d: skylight: no sight to sun", i );
continue;
}


Cache_t m_Cache[MAX_TEXTURE_POWER+1];
// Act like we didn't find any valid worldlights, so the shadow
BlockInfo_t m_Blocks[BLOCK_COUNT];
// manager uses the default shadow direction instead (should be the
unsigned int m_CurrentFrame;
// sun direction)
};


//-----------------------------------------------------------------------------
delete[] pvs;
// 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 )
return false;
// 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
// Calculate square distance to this worldlight
// unfortunate explicit management required for this render target
Vector vecDelta = light->origin - vecPosition;
// 32bpp edram is only largest shadow fragment, but resolved to actual shadow atlas
float flDistSqr = vecDelta.LengthSqr();
// because full-res 1024x1024 shadow buffer is too large for EDRAM
float flRadiusSqr = light->radius * light->radius;
m_TexturePage.InitRenderTargetTexture( TEXTURE_PAGE_SIZE, TEXTURE_PAGE_SIZE, RT_SIZE_NO_CHANGE, IMAGE_FORMAT_ARGB8888, MATERIAL_RT_DEPTH_NONE, false, "_rt_Shadows" );
 
// Skip lights that are out of our radius
if ( flRadiusSqr > 0 && flDistSqr >= flRadiusSqr )
{
//engine->Con_NPrintf( i, "%d: out-of-radius (dist: %d, radius: %d)", i, sqrt( flDistSqr ), light->radius );
continue;
}
 
// Is it out of our PVS?
if ( !g_pEngineServer->CheckOriginInPVS( light->origin, pvs, nPVSSize ) )
{
//engine->Con_NPrintf( i, "%d: out of PVS", i );
continue;
}
 
// Calculate intensity at our position
float flRatio = Engine_WorldLightDistanceFalloff( light, vecDelta );
Vector vecIntensity = light->intensity * flRatio;
 
// Is this light more intense than the one we already found?
if ( vecIntensity.LengthSqr() <= vecLightBrightness.LengthSqr() )
{
//engine->Con_NPrintf( i, "%d: too dim", i );
continue;
}
 
// Can we see the light?
trace_t tr;
Vector vecAbsStart = vecPosition + Vector( 0, 0, 30 );
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;


// edram footprint is only 256x256x4 = 256K
//engine->Con_NPrintf( i, "%d: set (%.2f)", i, vecIntensity.Length() );
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
delete[] pvs;
// 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()
//engine->Con_NPrintf( m_nWorldLights, "result: %d", !vecLightBrightness.IsZero() );
{
return !vecLightBrightness.IsZero();
m_TexturePage.Shutdown();
}
}


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


m_Textures.EnsureCapacity(256);
// memdbgon must be the last include file in a .cpp file!!!
m_Fragments.EnsureCapacity(256);
#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" );


// Set up the block sizes....
#if defined( _X360 )
// FIXME: Improve heuristic?!?
ConVar r_flashlightdepthres( "r_flashlightdepthres", "512" );
#if !defined( _X360 )
m_Blocks[0].m_FragmentPower  = MAX_TEXTURE_POWER-4; // 128 cells at ExE resolution
#else
#else
m_Blocks[0].m_FragmentPower  = MAX_TEXTURE_POWER-3; // 64 cells at DxD resolution
ConVar r_flashlightdepthres( "r_flashlightdepthres", "1024" );
#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;


// Initialize the LRU
ConVar r_threaded_client_shadow_manager( "r_threaded_client_shadow_manager", "0" );
int i;
 
for ( i = 0; i <= MAX_TEXTURE_POWER; ++i )
#ifdef _WIN32
{
#pragma warning( disable: 4701 )
m_Cache[i].m_List = m_Fragments.CreateList();
#endif
}


// Now that the block sizes are allocated, create LRUs for the various block sizes
// forward declarations
for ( i = 0; i < BLOCK_COUNT; ++i)
void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );
{
// 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
// 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
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CTextureAllocator::DebugPrintCache( void )
typedef unsigned short TextureHandle_t;
enum
{
{
// For each fragment
INVALID_TEXTURE_HANDLE = (TextureHandle_t)~0
int nNumFragments = m_Fragments.TotalCount();
};
int nNumInvalidFragments = 0;


Warning("Fragments (%d):\n===============\n", nNumFragments);
class CTextureAllocator
 
{
for ( int f = 0; f < nNumFragments; f++ )
public:
{
// Initialize the allocator with something that knows how to refresh the bits
if ( ( m_Fragments[f].m_FrameUsed != 0 ) && ( m_Fragments[f].m_Texture != INVALID_TEXTURE_HANDLE ) )
void Init();
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 );
void Shutdown();
else
nNumInvalidFragments++;
}


Warning("Invalid Fragments: %d\n", nNumInvalidFragments);
// Resets the allocator
void Reset();


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


}
// 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...
// Adds a block worth of fragments to the LRU
void AdvanceFrame();
//-----------------------------------------------------------------------------
void CTextureAllocator::AddBlockToLRU( int block )
{
int power = m_Blocks[block].m_FragmentPower;
int size = (1 << power);


// Compute the number of fragments in this block
// Get at the location of the texture
int fragmentCount = MAX_TEXTURE_SIZE / size;
void GetTextureRect(TextureHandle_t handle, int& x, int& y, int& w, int& h );
fragmentCount *= fragmentCount;


// For each fragment, indicate which block it's a part of (and the index)
// Get at the texture it's a part of
// and then stick in at the top of the LRU
ITexture *GetTexture();
while (--fragmentCount >= 0 )
{
// Get at the total texture size.
FragmentHandle_t f = m_Fragments.Alloc( );
void GetTotalTextureSize( int& w, int& h );
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 );
}
}


void DebugPrintCache( void );


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


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
// Mark something as being used (MRU)..
{
//-----------------------------------------------------------------------------
FragmentHandle_t m_Fragment;
void CTextureAllocator::MarkUsed( FragmentHandle_t fragment )
unsigned short m_Size;
{
unsigned short m_Power;
int block = m_Fragments[fragment].m_Block;
};
int power = m_Blocks[block].m_FragmentPower;


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


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


//-----------------------------------------------------------------------------
struct BlockInfo_t
// Mark something as being unused (LRU)..
{
//-----------------------------------------------------------------------------
unsigned short m_FragmentPower;
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
struct Cache_t
Cache_t& cache = m_Cache[power];
{
m_Fragments.LinkToHead( cache.m_List, fragment );
unsigned short m_List;
}
};


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


//-----------------------------------------------------------------------------
// Unlink fragment from cache
// Allocate, deallocate texture
void UnlinkFragmentFromCache( Cache_t& cache, FragmentHandle_t fragment );
//-----------------------------------------------------------------------------
TextureHandle_t CTextureAllocator::AllocateTexture( int w, int h )
{
// Implementational detail for now
Assert( w == h );


// Clamp texture size
// Mark something as being used (MRU)..
if (w < MIN_TEXTURE_SIZE)
void MarkUsed( FragmentHandle_t fragment );
w = MIN_TEXTURE_SIZE;
else if (w > MAX_TEXTURE_SIZE)
w = MAX_TEXTURE_SIZE;


TextureHandle_t handle = m_Textures.AddToTail();
// Mark something as being unused (LRU)..
m_Textures[handle].m_Fragment = INVALID_FRAGMENT_HANDLE;
void MarkUnused( FragmentHandle_t fragment );
m_Textures[handle].m_Size = w;


// Find the power of two
// Disconnect texture from fragment
int power = 0;
void DisconnectTextureFromFragment( FragmentHandle_t f );
int size = 1;
while(size < w)
{
size <<= 1;
++power;
}
Assert( size == w );


m_Textures[handle].m_Power = power;
// Returns the size of a particular fragment
int GetFragmentPower( FragmentHandle_t f ) const;


return handle;
// 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;
};


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


#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 );


// Warning( "End of DisconnectTextureFromFragment\n" );
// due to texture/surface size mismatch, ensure texture page is entirely cleared translucent
// DebugPrintCache();
// 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 )
{
{
TextureInfo_t& info = m_Textures[h];
m_TexturePage.Shutdown();
FragmentHandle_t currentFragment = info.m_Fragment;
return (currentFragment != INVALID_FRAGMENT_HANDLE);
}
}




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


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


// spin up to the best fragment size
// Set up the block sizes....
int nDesiredPower = MIN_TEXTURE_POWER;
// FIXME: Improve heuristic?!?
int nDesiredWidth = MIN_TEXTURE_SIZE;
#if !defined( _X360 )
while ( (nDesiredWidth * nDesiredWidth) < flArea )
m_Blocks[0].m_FragmentPower  = MAX_TEXTURE_POWER-4; // 128 cells at ExE resolution
{
#else
if ( nDesiredPower >= info.m_Power )
m_Blocks[0].m_FragmentPower  = MAX_TEXTURE_POWER-3; // 64 cells at DxD resolution
{
#endif
nDesiredPower = info.m_Power;
m_Blocks[1].m_FragmentPower  = MAX_TEXTURE_POWER-3; // 64 cells at DxD resolution
break;
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
++nDesiredPower;
m_Blocks[5].m_FragmentPower  = MAX_TEXTURE_POWER-1;
nDesiredWidth <<= 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 we've got a valid fragment for this texture, no worries!
// Now that the block sizes are allocated, create LRUs for the various block sizes
int nCurrentPower = -1;
for ( i = 0; i < BLOCK_COUNT; ++i)
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
// Initialize LRU
nCurrentPower = GetFragmentPower(info.m_Fragment);
AddBlockToLRU( i );
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" );
m_CurrentFrame = 0;
// DebugPrintCache();
}


// Grab the LRU fragment from the appropriate cache
void CTextureAllocator::DeallocateAllTextures()
// If that fragment is connected to a texture, disconnect it.
{
int power = nDesiredPower;
m_Textures.Purge();
 
m_Fragments.Purge();
FragmentHandle_t f = INVALID_FRAGMENT_HANDLE;
for ( int i = 0; i <= MAX_TEXTURE_POWER; ++i )
bool done = false;
while (!done && power >= 0)
{
{
f = m_Fragments.Head( m_Cache[power].m_List );
m_Cache[i].m_List = m_Fragments.InvalidIndex();
// 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();
// 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);


// Ok, lets see if we're better off than we were...
for ( int f = 0; f < nNumFragments; f++ )
if (currentFragment != INVALID_FRAGMENT_HANDLE)
{
{
if (power <= nCurrentPower)
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 );
// Oops... we're not. Let's leave well enough alone
else
// Move to the back of the LRU
nNumInvalidFragments++;
MarkUsed( currentFragment );
}
return false;
 
}
Warning("Invalid Fragments: %d\n", nNumInvalidFragments);
else
{
// Clear out the old fragment
DisconnectTextureFromFragment(currentFragment);
}
}


if ( f == INVALID_FRAGMENT_HANDLE )
// for ( int c = 0; c <= MAX_TEXTURE_POWER; ++c )
{
// {
return false;
// Warning("Cache Index (%d)\n", m_Cache[c].m_List);
}
// }
 
// 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
// Adds a block worth of fragments to the LRU
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CTextureAllocator::GetFragmentPower( FragmentHandle_t f ) const
void CTextureAllocator::AddBlockToLRU( int block )
{
{
return m_Blocks[m_Fragments[f].m_Block].m_FragmentPower;
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)
// Advance frame...
// and then stick in at the top of the LRU
//-----------------------------------------------------------------------------
while (--fragmentCount >= 0 )
void CTextureAllocator::AdvanceFrame()
{
{
FragmentHandle_t f = m_Fragments.Alloc( );
// Be sure that this is called as infrequently as possible (i.e. once per frame,
m_Fragments[f].m_Block = block;
// NOT once per view) to prevent cache thrash when rendering multiple views in a single frame
m_Fragments[f].m_Index = fragmentCount;
m_CurrentFrame++;
m_Fragments[f].m_Texture = INVALID_TEXTURE_HANDLE;
m_Fragments[f].m_FrameUsed = 0xFFFFFFFF;
m_Fragments.LinkToHead( m_Cache[power].m_List, f );
}
}
}




//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Prepare to render into texture...
// Unlink fragment from cache
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
ITexture* CTextureAllocator::GetTexture()
void CTextureAllocator::UnlinkFragmentFromCache( Cache_t& cache, FragmentHandle_t fragment )
{
{
return m_TexturePage;
m_Fragments.Unlink( cache.m_List, fragment);
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Get at the total texture size.
// Mark something as being used (MRU)..
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CTextureAllocator::GetTotalTextureSize( int& w, int& h )
void CTextureAllocator::MarkUsed( FragmentHandle_t fragment )
{
{
w = h = TEXTURE_PAGE_SIZE;
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;
}
}




//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Returns the rectangle the texture lives in..
// Mark something as being unused (LRU)..
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CTextureAllocator::GetTextureRect(TextureHandle_t handle, int& x, int& y, int& w, int& h )
void CTextureAllocator::MarkUnused( FragmentHandle_t fragment )
{
{
TextureInfo_t& info = m_Textures[handle];
int block = m_Fragments[fragment].m_Block;
Assert( info.m_Fragment != INVALID_FRAGMENT_HANDLE );
int power = m_Blocks[block].m_FragmentPower;


// Compute the position of the fragment in the page
// Hook it at the end of the LRU
FragmentInfo_t& fragment = m_Fragments[info.m_Fragment];
Cache_t& cache = m_Cache[power];
int blockY = fragment.m_Block / BLOCKS_PER_ROW;
m_Fragments.LinkToHead( cache.m_List, fragment );
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...
// Allocate, deallocate texture
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#define TEXEL_SIZE_PER_CASTER_SIZE 2.0f
TextureHandle_t CTextureAllocator::AllocateTexture( int w, int h )
#define MAX_FALLOFF_AMOUNT 240
{
#define MAX_CLIP_PLANE_COUNT 4
// Implementational detail for now
#define SHADOW_CULL_TOLERANCE 0.5f
Assert( w == h );


static ConVar r_shadows( "r_shadows", "1" ); // hook into engine's cvars..
// Clamp texture size
static ConVar r_shadowmaxrendered("r_shadowmaxrendered", "32");
if (w < MIN_TEXTURE_SIZE)
static ConVar r_shadows_gamecontrol( "r_shadows_gamecontrol", "-1", FCVAR_CHEAT ); // hook into engine's cvars..
w = MIN_TEXTURE_SIZE;
else if (w > MAX_TEXTURE_SIZE)
w = MAX_TEXTURE_SIZE;


//-----------------------------------------------------------------------------
TextureHandle_t handle = m_Textures.AddToTail();
// The class responsible for dealing with shadows on the client side
m_Textures[handle].m_Fragment = INVALID_FRAGMENT_HANDLE;
// Oh, and let's take a moment and notice how happy Robin and John must be
m_Textures[handle].m_Size = w;
// 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"; }
// Find the power of two
int power = 0;
int size = 1;
while(size < w)
{
size <<= 1;
++power;
}
Assert( size == w );


// Inherited from IClientShadowMgr
m_Textures[handle].m_Power = power;
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; }
return handle;
}


virtual void PreRender();
void CTextureAllocator::DeallocateTexture( TextureHandle_t h )
virtual void Update( float frametime ) { }
{
virtual void PostRender() {}
// Warning("Beginning of DeallocateTexture\n");
// DebugPrintCache();


virtual void OnSave() {}
if (m_Textures[h].m_Fragment != INVALID_FRAGMENT_HANDLE)
virtual void OnRestore() {}
{
virtual void SafeRemoveIfDesired() {}
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);


virtual ClientShadowHandle_t CreateShadow( ClientEntityHandle_t entity, int flags );
// Warning("End of DeallocateTexture\n");
virtual void DestroyShadow( ClientShadowHandle_t handle );
// DebugPrintCache();
}


// 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 );
// Disconnect texture from fragment
//-----------------------------------------------------------------------------
void CTextureAllocator::DisconnectTextureFromFragment( FragmentHandle_t f )
{
// Warning( "Beginning of DisconnectTextureFromFragment\n" );
// DebugPrintCache();


void ComputeBoundingSphere( IClientRenderable* pRenderable, Vector& origin, float& radius );
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;
}


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
// Warning( "End of DisconnectTextureFromFragment\n" );
virtual void MarkRenderToTextureShadowDirty( ClientShadowHandle_t handle );
// DebugPrintCache();
 
}
// 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 );
// 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);
}


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


// Returns the shadow texture
TextureInfo_t& info = m_Textures[h];
ITexture* GetShadowTexture( unsigned short h );


// Returns shadow information
// spin up to the best fragment size
const ShadowInfo_t& GetShadowInfo( ClientShadowHandle_t h );
int nDesiredPower = MIN_TEXTURE_POWER;
int nDesiredWidth = MIN_TEXTURE_SIZE;
while ( (nDesiredWidth * nDesiredWidth) < flArea )
{
if ( nDesiredPower >= info.m_Power )
{
nDesiredPower = info.m_Power;
break;
}


// Renders the shadow texture to screen...
++nDesiredPower;
void RenderShadowTexture( int w, int h );
nDesiredWidth <<= 1;
}


// Sets the shadow direction
// If we've got a valid fragment for this texture, no worries!
virtual void SetShadowDirection( const Vector& dir );
int nCurrentPower = -1;
const Vector &GetShadowDirection() const;
FragmentHandle_t currentFragment = info.m_Fragment;
 
if (currentFragment != INVALID_FRAGMENT_HANDLE)
// Sets the shadow color
{
virtual void SetShadowColor( unsigned char r, unsigned char g, unsigned char b );
// If the current fragment is at or near the desired power, we're done
void GetShadowColor( unsigned char *r, unsigned char *g, unsigned char *b ) const;
nCurrentPower = GetFragmentPower(info.m_Fragment);
 
Assert( nCurrentPower <= info.m_Power );
// Sets the shadow distance
bool bShouldKeepTexture = (!bWillRedraw) && (nDesiredPower < 8) && (nDesiredPower - nCurrentPower <= 1);
virtual void SetShadowDistance( float flMaxDistance );
if ((nCurrentPower == nDesiredPower) || bShouldKeepTexture)
float GetShadowDistance( ) const;
{
 
// Move to the back of the LRU
// Sets the screen area at which blobby shadows are always used
MarkUsed( currentFragment );
virtual void SetShadowBlobbyCutoffArea( float flMinArea );
return false;
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 );
// Warning( "\n\nUseTexture B\n" );
void SetShadowFromWorldLightsEnabled( bool bEnabled );
// DebugPrintCache();
bool IsShadowingFromWorldLights() const { return m_bShadowFromWorldLights; }


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


enum
FragmentHandle_t f = INVALID_FRAGMENT_HANDLE;
bool done = false;
while (!done && power >= 0)
{
{
SHADOW_FLAGS_TEXTURE_DIRTY = (CLIENT_SHADOW_FLAGS_LAST_FLAG << 1),
f = m_Fragments.Head( m_Cache[power].m_List );
SHADOW_FLAGS_BRUSH_MODEL = (CLIENT_SHADOW_FLAGS_LAST_FLAG << 2),  
SHADOW_FLAGS_USING_LOD_SHADOW = (CLIENT_SHADOW_FLAGS_LAST_FLAG << 3),
// This represents an overflow condition (used too many textures of
SHADOW_FLAGS_LIGHT_WORLD = (CLIENT_SHADOW_FLAGS_LAST_FLAG << 4),
// 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();


struct ClientShadow_t
// Ok, lets see if we're better off than we were...
if (currentFragment != INVALID_FRAGMENT_HANDLE)
{
{
ClientEntityHandle_t m_Entity;
if (power <= nCurrentPower)
ShadowHandle_t m_ShadowHandle;
{
ClientLeafShadowHandle_t m_ClientLeafShadowHandle;
// Oops... we're not. Let's leave well enough alone
unsigned short m_Flags;
// Move to the back of the LRU
VMatrix m_WorldToShadow;
MarkUsed( currentFragment );
Vector2D m_WorldSize;
return false;
Vector m_ShadowDir;
}
Vector m_LastOrigin;
else
QAngle m_LastAngles;
{
Vector m_CurrentLightPos; // When shadowing from local lights, stores the position of the currently shadowing light
// Clear out the old fragment
Vector m_TargetLightPos; // When shadowing from local lights, stores the position of the new shadowing light
DisconnectTextureFromFragment(currentFragment);
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:
if ( f == INVALID_FRAGMENT_HANDLE )
// Shadow update functions
{
void UpdateStudioShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle );
return false;
void UpdateBrushShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle );
}
void UpdateShadow( ClientShadowHandle_t handle, bool force );


// Gets the entity whose shadow this shadow will render into
// Disconnect existing texture from this fragment (if necessary)
IClientRenderable *GetParentShadowEntity( ClientShadowHandle_t handle );
DisconnectTextureFromFragment(f);


// Adds the child bounds to the bounding box
// Connnect new texture to this fragment
void AddChildBounds( matrix3x4_t &matWorldToBBox, IClientRenderable* pParent, Vector &vecMins, Vector &vecMaxs );
info.m_Fragment = f;
m_Fragments[f].m_Texture = h;


// Compute a bounds for the entity + children
// Move to the back of the LRU
void ComputeHierarchicalBounds( IClientRenderable *pRenderable, Vector &vecMins, Vector &vecMaxs );
MarkUsed( f );


// Builds matrices transforming from world space to shadow space
// Indicate we need a redraw
void BuildGeneralWorldToShadowMatrix( VMatrix& matWorldToShadow,
return true;
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 );
//-----------------------------------------------------------------------------
// Returns the size of a particular fragment
//-----------------------------------------------------------------------------
int CTextureAllocator::GetFragmentPower( FragmentHandle_t f ) const
{
return m_Blocks[m_Fragments[f].m_Block].m_FragmentPower;
}


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


// Compute the shadow origin and attenuation start distance
//-----------------------------------------------------------------------------
float ComputeLocalShadowOrigin( IClientRenderable* pRenderable,  
// Advance frame...
const Vector& mins, const Vector& maxs, const Vector& localShadowDir, float backupFactor, Vector& origin );
//-----------------------------------------------------------------------------
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++;
}


// 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;
// Prepare to render into texture...
ShadowType_t GetActualShadowCastType( IClientRenderable *pRenderable ) const;
//-----------------------------------------------------------------------------
ITexture* CTextureAllocator::GetTexture()
{
return m_TexturePage;
}


// Builds a simple blobby shadow
//-----------------------------------------------------------------------------
void BuildOrthoShadow( IClientRenderable* pRenderable, ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs);
// Get at the total texture size.
//-----------------------------------------------------------------------------
void CTextureAllocator::GetTotalTextureSize( int& w, int& h )
{
w = h = TEXTURE_PAGE_SIZE;
}


// 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 );
// 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 );


// Does all the lovely stuff we need to do to have render-to-texture shadows
// Compute the position of the fragment in the page
void SetupRenderToTextureShadow( ClientShadowHandle_t h );
FragmentInfo_t& fragment = m_Fragments[info.m_Fragment];
void CleanUpRenderToTextureShadow( ClientShadowHandle_t h );
int blockY = fragment.m_Block / BLOCKS_PER_ROW;
int blockX = fragment.m_Block - blockY * BLOCKS_PER_ROW;


// Compute the extra shadow planes
int fragmentSize = (1 << m_Blocks[fragment.m_Block].m_FragmentPower);
void ComputeExtraClipPlanes( IClientRenderable* pRenderable,
int fragmentsPerRow = BLOCK_SIZE / fragmentSize;
ClientShadowHandle_t handle, const Vector* vec,
int fragmentY = fragment.m_Index / fragmentsPerRow;
const Vector& mins, const Vector& maxs, const Vector& localShadowDir );
int fragmentX = fragment.m_Index - fragmentY * fragmentsPerRow;


// Set extra clip planes related to shadows...
x = blockX * BLOCK_SIZE + fragmentX * fragmentSize;
void ClearExtraClipPlanes( ClientShadowHandle_t h );
y = blockY * BLOCK_SIZE + fragmentY * fragmentSize;
void AddExtraClipPlane( ClientShadowHandle_t h, const Vector& normal, float dist );
w = fragmentSize;
h = fragmentSize;
}


// 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 );
//-----------------------------------------------------------------------------
// 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


// Causes all shadows to be re-updated
static ConVar r_shadows( "r_shadows", "1" ); // hook into engine's cvars..
void UpdateAllShadows();
static ConVar r_shadowmaxrendered("r_shadowmaxrendered", "32");
static ConVar r_shadows_gamecontrol( "r_shadows_gamecontrol", "-1", FCVAR_CHEAT ); // hook into engine's cvars..


// One of these gets called with every shadow that potentially will need to re-render
//-----------------------------------------------------------------------------
bool DrawRenderToTextureShadow( unsigned short clientShadowHandle, float flArea );
// The class responsible for dealing with shadows on the client side
void DrawRenderToTextureShadowLOD( unsigned short clientShadowHandle );
// 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();


// Draws all children shadows into our own
virtual char const *Name() { return "CCLientShadowMgr"; }
bool DrawShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild = false );


// Setup stage for threading
// Inherited from IClientShadowMgr
bool BuildSetupListForRenderToTextureShadow( unsigned short clientShadowHandle, float flArea );
virtual bool Init();
bool BuildSetupShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild = false );
virtual void PostInit() {}
virtual void Shutdown();
virtual void LevelInitPreEntity();
virtual void LevelInitPostEntity() {}
virtual void LevelShutdownPreEntity() {}
virtual void LevelShutdownPostEntity();


// Computes + sets the render-to-texture texcoords
virtual bool IsPerFrame() { return true; }
void SetRenderToTextureShadowTexCoords( ShadowHandle_t handle, int x, int y, int w, int h );


// Visualization....
virtual void PreRender();
void DrawRenderToTextureDebugInfo( IClientRenderable* pRenderable, const Vector& mins, const Vector& maxs );
virtual void Update( float frametime ) { }
virtual void PostRender() {}


// Advance frame
virtual void OnSave() {}
void AdvanceFrame();
virtual void OnRestore() {}
virtual void SafeRemoveIfDesired() {}


// Returns renderable-specific shadow info
virtual ClientShadowHandle_t CreateShadow( ClientEntityHandle_t entity, int flags );
float GetShadowDistance( IClientRenderable *pRenderable ) const;
virtual void DestroyShadow( ClientShadowHandle_t handle );
const Vector &GetShadowDirection( IClientRenderable *pRenderable ) const;
const Vector &GetShadowDirection( ClientShadowHandle_t shadowHandle ) const;


// Initialize, shutdown render-to-texture shadows
// Create flashlight (projected texture light source)
void InitDepthTextureShadows();
virtual ClientShadowHandle_t CreateFlashlight( const FlashlightState_t &lightState );
void ShutdownDepthTextureShadows();
virtual void UpdateFlashlightState( ClientShadowHandle_t shadowHandle, const FlashlightState_t &lightState );
virtual void DestroyFlashlight( ClientShadowHandle_t shadowHandle );


// Initialize, shutdown render-to-texture shadows
// Update a shadow
void InitRenderToTextureShadows();
virtual void UpdateProjectedTexture( ClientShadowHandle_t handle, bool force );
void ShutdownRenderToTextureShadows();


static bool ShadowHandleCompareFunc( const ClientShadowHandle_t& lhs, const ClientShadowHandle_t& rhs )
void ComputeBoundingSphere( IClientRenderable* pRenderable, Vector& origin, float& radius );
{
return lhs < rhs;
}


ClientShadowHandle_t CreateProjectedTexture( ClientEntityHandle_t entity, int flags );
virtual void AddToDirtyShadowList( ClientShadowHandle_t handle, bool bForce );
virtual void AddToDirtyShadowList( IClientRenderable *pRenderable, bool force );


// Lock down the usage of a shadow depth texture...must be unlocked use on subsequent views / frames
// Marks the render-to-texture shadow as needing to be re-rendered
bool LockShadowDepthTexture( CTextureReference *shadowDepthTexture );
virtual void MarkRenderToTextureShadowDirty( ClientShadowHandle_t handle );
void UnlockAllShadowDepthTextures();


// Set and clear flashlight target renderable
// deals with shadows being added to shadow receivers
void SetFlashlightTarget( ClientShadowHandle_t shadowHandle, EHANDLE targetEntity );
void AddShadowToReceiver( ClientShadowHandle_t handle,
IClientRenderable* pRenderable, ShadowReceiver_t type );


// Set flashlight light world flag
// deals with shadows being added to shadow receivers
void SetFlashlightLightWorld( ClientShadowHandle_t shadowHandle, bool bLightWorld );
void RemoveAllShadowsFromReceiver( IClientRenderable* pRenderable, ShadowReceiver_t type );


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


// Builds a list of active shadows requiring shadow depth renders
// Kicks off rendering into shadow depth maps (if any)
int BuildActiveShadowDepthList( const CViewSetup &viewSetup, int nMaxDepthShadows, ClientShadowHandle_t *pActiveDepthShadows );
void ComputeShadowDepthTextures( const CViewSetup &view );


// Sets the view's active flashlight render state
// Frees shadow depth textures for use in subsequent view/frame
void SetViewFlashlightState( int nActiveFlashlightCount, ClientShadowHandle_t* pActiveFlashlights );
void FreeShadowDepthTextures();
void UpdateDirtyShadow( ClientShadowHandle_t handle );
void UpdateShadowDirectionFromLocalLightSource( ClientShadowHandle_t shadowHandle );


private:
// Returns the shadow texture
Vector m_SimpleShadowDir;
ITexture* GetShadowTexture( unsigned short h );
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;
// Returns shadow information
bool m_bRenderTargetNeedsClear;
const ShadowInfo_t& GetShadowInfo( ClientShadowHandle_t h );
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)
// Renders the shadow texture to screen...
// If either changes in a frame, PreRender() will catch it and do the appropriate allocation, deallocation or reallocation
void RenderShadowTexture( int w, int h );
bool m_bDepthTextureActive;
int m_nDepthTextureResolution; // Assume square (height == width)


CUtlVector< CTextureReference > m_DepthTextureCache;
// Sets the shadow direction
CUtlVector< bool > m_DepthTextureCacheLocks;
virtual void SetShadowDirection( const Vector& dir );
int m_nMaxDepthTextureShadows;
const Vector &GetShadowDirection() const;
bool m_bShadowFromWorldLights;


friend class CVisibleShadowList;
// Sets the shadow color
friend class CVisibleShadowFrustumList;
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
// Singleton
virtual void SetShadowDistance( float flMaxDistance );
//-----------------------------------------------------------------------------
float GetShadowDistance( ) const;
static CClientShadowMgr s_ClientShadowMgr;
IClientShadowMgr* g_pClientShadowMgr = &s_ClientShadowMgr;


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


//-----------------------------------------------------------------------------
// Set the darkness falloff bias
// Builds a list of potential shadows that lie within our PVS + view frustum
virtual void SetFalloffBias( ClientShadowHandle_t handle, unsigned char ucBias );
//-----------------------------------------------------------------------------
struct VisibleShadowInfo_t
{
ClientShadowHandle_t m_hShadow;
float m_flArea;
Vector m_vecAbsCenter;
};


class CVisibleShadowList : public IClientLeafShadowEnum
void RestoreRenderState();
{
public:


CVisibleShadowList();
// Computes a rough bounding box encompassing the volume of the shadow
int FindShadows( const CViewSetup *pView, int nLeafCount, LeafIndex_t *pLeafList );
    void ComputeShadowBBox( IClientRenderable *pRenderable, ClientShadowHandle_t shadowHandle, const Vector &vecAbsCenter, float flRadius, Vector *pAbsMins, Vector *pAbsMaxs );
int GetVisibleShadowCount() const;


const VisibleShadowInfo_t &GetVisibleShadow( int i ) const;
bool WillParentRenderBlobbyShadow( IClientRenderable *pRenderable );


private:
// Are we the child of a shadow with render-to-texture?
void EnumShadow( unsigned short clientShadowHandle );
bool ShouldUseParentShadow( IClientRenderable *pRenderable );
float ComputeScreenArea( const Vector &vecCenter, float r ) const;
void PrioritySort();


CUtlVector<VisibleShadowInfo_t> m_ShadowsInView;
void SetShadowsDisabled( bool bDisabled )
CUtlVector<int> m_PriorityIndex;
{
};
r_shadows_gamecontrol.SetValue( bDisabled != 1 );
}


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


//-----------------------------------------------------------------------------
private:
// Singleton instances of shadow and shadow frustum lists
enum
//-----------------------------------------------------------------------------
{
static CVisibleShadowList s_VisibleShadowList;
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;
static CUtlVector<C_BaseAnimating *> s_NPCShadowBoneSetups;
ShadowHandle_t m_ShadowHandle;
static CUtlVector<C_BaseAnimating *> s_NonNPCShadowBoneSetups;
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:
// CVisibleShadowList - Constructor and Accessors
// Shadow update functions
//-----------------------------------------------------------------------------
void UpdateStudioShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle );
CVisibleShadowList::CVisibleShadowList() : m_ShadowsInView( 0, 64 ), m_PriorityIndex( 0, 64 )  
void UpdateBrushShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle );
{
void UpdateShadow( ClientShadowHandle_t handle, bool force );
}


int CVisibleShadowList::GetVisibleShadowCount() const
// Gets the entity whose shadow this shadow will render into
{
IClientRenderable *GetParentShadowEntity( ClientShadowHandle_t handle );
return m_ShadowsInView.Count();
}


const VisibleShadowInfo_t &CVisibleShadowList::GetVisibleShadow( int i ) const
// Adds the child bounds to the bounding box
{
void AddChildBounds( matrix3x4_t &matWorldToBBox, IClientRenderable* pParent, Vector &vecMins, Vector &vecMaxs );
return m_ShadowsInView[m_PriorityIndex[i]];
}


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


//-----------------------------------------------------------------------------
// Builds matrices transforming from world space to shadow space
// CVisibleShadowList - Computes approximate screen area of the shadow
void BuildGeneralWorldToShadowMatrix( VMatrix& matWorldToShadow,
//-----------------------------------------------------------------------------
const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec );
float CVisibleShadowList::ComputeScreenArea( const Vector &vecCenter, float r ) const
{
CMatRenderContextPtr pRenderContext( materials );
float flScreenDiameter = pRenderContext->ComputePixelDiameterOfSphere( vecCenter, r );
return flScreenDiameter * flScreenDiameter;
}


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


//-----------------------------------------------------------------------------
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];


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


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


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


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


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


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


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


// FIXME: Add distance check here?
// 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 );


// Make sure it's in the frustum. If it isn't it's not interesting
// Compute the extra shadow planes
if (engine->CullBox( vecAbsMins, vecAbsMaxs ))
void ComputeExtraClipPlanes( IClientRenderable* pRenderable,
return;
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 );


int i = m_ShadowsInView.AddToTail( );
// Cull if the origin is on the wrong side of a shadow clip plane....
VisibleShadowInfo_t &info = m_ShadowsInView[i];
bool CullReceiver( ClientShadowHandle_t handle, IClientRenderable* pRenderable, IClientRenderable* pSourceRenderable );
info.m_hShadow = clientShadowHandle;
m_ShadowsInView[i].m_flArea = ComputeScreenArea( vecAbsCenter, flRadius );


// Har, har. When water is rendering (or any multipass technique),
bool ComputeSeparatingPlane( IClientRenderable* pRend1, IClientRenderable* pRend2, cplane_t* pPlane );
// 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;
}


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


//-----------------------------------------------------------------------------
// One of these gets called with every shadow that potentially will need to re-render
// CVisibleShadowList - Sort based on screen area/priority
bool DrawRenderToTextureShadow( unsigned short clientShadowHandle, float flArea );
//-----------------------------------------------------------------------------
void DrawRenderToTextureShadowLOD( unsigned short clientShadowHandle );
void CVisibleShadowList::PrioritySort()
{
int nCount = m_ShadowsInView.Count();
m_PriorityIndex.EnsureCapacity( nCount );


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


int i, j;
// Setup stage for threading
for ( i = 0; i < nCount; ++i )
bool BuildSetupListForRenderToTextureShadow( unsigned short clientShadowHandle, float flArea );
{
bool BuildSetupShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild = false );
m_PriorityIndex.AddToTail(i);
}


for ( i = 0; i < nCount - 1; ++i )
// Computes + sets the render-to-texture texcoords
{
void SetRenderToTextureShadowTexCoords( ShadowHandle_t handle, int x, int y, int w, int h );
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;
}
}
swap( m_PriorityIndex[i], m_PriorityIndex[nLargestInd] );
}
}


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


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


m_ShadowsInView.RemoveAll();
// Returns renderable-specific shadow info
ClientLeafSystem()->EnumerateShadowsInLeaves( nLeafCount, pLeafList, this );
float GetShadowDistance( IClientRenderable *pRenderable ) const;
int nCount = m_ShadowsInView.Count();
const Vector &GetShadowDirection( IClientRenderable *pRenderable ) const;
if (nCount != 0)
const Vector &GetShadowDirection( ClientShadowHandle_t shadowHandle ) const;
{
// Sort based on screen area/priority
PrioritySort();
}
return nCount;
}


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


//-----------------------------------------------------------------------------
// Initialize, shutdown render-to-texture shadows
// Constructor
void InitRenderToTextureShadows();
//-----------------------------------------------------------------------------
void ShutdownRenderToTextureShadows();
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();
}


 
static bool ShadowHandleCompareFunc( const ClientShadowHandle_t& lhs, const ClientShadowHandle_t& rhs )
//-----------------------------------------------------------------------------
// 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();
return lhs < rhs;
Msg( "%.2f %.2f %.2f\n", dir.x, dir.y, dir.z );
return;
}
}


if ( args.ArgC() == 4 )
ClientShadowHandle_t CreateProjectedTexture( ClientEntityHandle_t entity, int flags );
{
 
dir.x = atof( args[1] );
// Lock down the usage of a shadow depth texture...must be unlocked use on subsequent views / frames
dir.y = atof( args[2] );
bool LockShadowDepthTexture( CTextureReference *shadowDepthTexture );
dir.z = atof( args[3] );
void UnlockAllShadowDepthTextures();
s_ClientShadowMgr.SetShadowDirection(dir);
}
}


CON_COMMAND_F( r_shadowangles, "Set shadow angles", FCVAR_CHEAT )
// Set and clear flashlight target renderable
{
void SetFlashlightTarget( ClientShadowHandle_t shadowHandle, EHANDLE targetEntity );
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)
// Set flashlight light world flag
{
void SetFlashlightLightWorld( ClientShadowHandle_t shadowHandle, bool bLightWorld );
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 )
bool IsFlashlightTarget( ClientShadowHandle_t shadowHandle, IClientRenderable *pRenderable );
{
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)
// Builds a list of active shadows requiring shadow depth renders
{
int BuildActiveShadowDepthList( const CViewSetup &viewSetup, int nMaxDepthShadows, ClientShadowHandle_t *pActiveDepthShadows );
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 )
// Sets the view's active flashlight render state
{
void SetViewFlashlightState( int nActiveFlashlightCount, ClientShadowHandle_t* pActiveFlashlights );
if (args.ArgC() == 1)
void UpdateDirtyShadow( ClientShadowHandle_t handle );
{
    void UpdateShadowDirectionFromLocalLightSource( ClientShadowHandle_t shadowHandle );
float flDist = s_ClientShadowMgr.GetShadowDistance( );
Msg( "Shadow distance %.2f\n", flDist );
return;
}


if (args.ArgC() == 2)
private:
{
Vector m_SimpleShadowDir;
float flDistance = atof( args[1] );
color32 m_AmbientLightColor;
s_ClientShadowMgr.SetShadowDistance( flDistance );
CMaterialReference m_SimpleShadow;
}
CMaterialReference m_RenderShadow;
}
CMaterialReference m_RenderModelShadow;
CTextureReference m_DummyColorTexture;
CUtlLinkedList< ClientShadow_t, ClientShadowHandle_t > m_Shadows;
CTextureAllocator m_ShadowAllocator;


CON_COMMAND_F( r_shadowblobbycutoff, "some shadow stuff", FCVAR_CHEAT )
bool m_RenderToTextureActive;
{
bool m_bRenderTargetNeedsClear;
if (args.ArgC() == 1)
bool m_bUpdatingDirtyShadows;
{
bool m_bThreaded;
float flArea = s_ClientShadowMgr.GetBlobbyCutoffArea( );
float m_flShadowCastDist;
Msg( "Cutoff area %.2f\n", flArea );
float m_flMinShadowArea;
return;
CUtlRBTree< ClientShadowHandle_t, unsigned short > m_DirtyShadows;
}
CUtlVector< ClientShadowHandle_t > m_TransparentShadows;


if (args.ArgC() == 2)
// 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
float flArea = atof( args[1] );
bool m_bDepthTextureActive;
s_ClientShadowMgr.SetShadowBlobbyCutoffArea( flArea );
int m_nDepthTextureResolution; // Assume square (height == width)
}
bool m_bShadowFromWorldLights;
}
 
CUtlVector< CTextureReference > m_DepthTextureCache;
CUtlVector< bool > m_DepthTextureCacheLocks;
int m_nMaxDepthTextureShadows;


static void ShadowRestoreFunc( int nChangeFlags )
friend class CVisibleShadowList;
{
friend class CVisibleShadowFrustumList;
s_ClientShadowMgr.RestoreRenderState();
};
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Initialization, shutdown
// Singleton
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CClientShadowMgr::Init()
static CClientShadowMgr s_ClientShadowMgr;
{
IClientShadowMgr* g_pClientShadowMgr = &s_ClientShadowMgr;
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 );
//-----------------------------------------------------------------------------
// 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;
};


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


bool bLowEnd = ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 );
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();


if ( !bLowEnd && r_shadowrendertotexture.GetBool() )
CUtlVector<VisibleShadowInfo_t> m_ShadowsInView;
{
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 )
{
}
}


void CClientShadowMgr::Shutdown()
int CVisibleShadowList::GetVisibleShadowCount() const
{
{
m_SimpleShadow.Shutdown();
return m_ShadowsInView.Count();
m_Shadows.RemoveAll();
}
ShutdownRenderToTextureShadows();
 
const VisibleShadowInfo_t &CVisibleShadowList::GetVisibleShadow( int i ) const
{
return m_ShadowsInView[m_PriorityIndex[i]];
}


ShutdownDepthTextureShadows();


materials->RemoveRestoreFunc( ShadowRestoreFunc );
//-----------------------------------------------------------------------------
// 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;
}
}




//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Initialize, shutdown depth-texture shadows
// CVisibleShadowList - Visits every shadow in the list of leaves
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::InitDepthTextureShadows()
void CVisibleShadowList::EnumShadow( unsigned short clientShadowHandle )
{
{
VPROF_BUDGET( "CClientShadowMgr::InitDepthTextureShadows", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
CClientShadowMgr::ClientShadow_t& shadow = s_ClientShadowMgr.m_Shadows[clientShadowHandle];


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


ImageFormat dstFormat  = materials->GetShadowDepthTextureFormat(); // Vendor-dependent depth texture format
// We don't need to bother with it if it's not render-to-texture
#if !defined( _X360 )
if ( s_ClientShadowMgr.GetActualShadowCastType( clientShadowHandle ) != SHADOWS_RENDER_TO_TEXTURE )
ImageFormat nullFormat = materials->GetNullTextureFormat(); // Vendor-dependent null texture format (takes as little memory as possible)
return;
#endif
materials->BeginRenderTargetAllocation();


#if defined( _X360 )
// Don't bother with it if the shadow is totally transparent
// For the 360, we'll be rendering depth directly into the dummy depth and Resolve()ing to the depth texture.
const ShadowInfo_t &shadowInfo = shadowmgr->GetInfo( shadow.m_ShadowHandle );
// only need the dummy surface, don't care about color results
if ( shadowInfo.m_FalloffBias == 255 )
m_DummyColorTexture.InitRenderTargetTexture( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), RT_SIZE_OFFSCREEN, IMAGE_FORMAT_BGR565, MATERIAL_RT_DEPTH_SHARED, false, "_rt_ShadowDummy" );
return;
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
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
m_DepthTextureCache.Purge();
Assert( pRenderable );
m_DepthTextureCacheLocks.Purge();
for( int i=0; i < m_nMaxDepthTextureShadows; i++ )
{
CTextureReference depthTex; // Depth-stencil surface
bool bFalse = false;


char strRTName[64];
// Don't bother with children of hierarchy; they will be drawn with their parents
sprintf( strRTName, "_rt_ShadowDepthTexture_%d", i );
if ( s_ClientShadowMgr.ShouldUseParentShadow( pRenderable ) || s_ClientShadowMgr.WillParentRenderBlobbyShadow( pRenderable ) )
return;


#if defined( _X360 )
// Compute a sphere surrounding the shadow
// create a render target to use as a resolve target to get the shared depth buffer
// FIXME: This doesn't account for children of hierarchy... too bad!
// surface is effectively never used
Vector vecAbsCenter;
depthTex.InitRenderTargetTexture( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_OFFSCREEN, dstFormat, MATERIAL_RT_DEPTH_NONE, false, strRTName );
float flRadius;
depthTex.InitRenderTargetSurface( 1, 1, dstFormat, false );
s_ClientShadowMgr.ComputeBoundingSphere( pRenderable, vecAbsCenter, flRadius );
#else
depthTex.InitRenderTarget( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_OFFSCREEN, dstFormat, MATERIAL_RT_DEPTH_NONE, false, strRTName );
#endif


if ( i == 0 )
// Compute a box surrounding the shadow
{
Vector vecAbsMins, vecAbsMaxs;
// Shadow may be resized during allocation (due to resolution constraints etc)
s_ClientShadowMgr.ComputeShadowBBox( pRenderable, shadow.m_ShadowHandle, vecAbsCenter, flRadius, &vecAbsMins, &vecAbsMaxs );
m_nDepthTextureResolution = depthTex->GetActualWidth();
r_flashlightdepthres.SetValue( m_nDepthTextureResolution );
}


m_DepthTextureCache.AddToTail( depthTex );
// FIXME: Add distance check here?
m_DepthTextureCacheLocks.AddToTail( bFalse );
}


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


void CClientShadowMgr::ShutdownDepthTextureShadows()  
int i = m_ShadowsInView.AddToTail( );
{
VisibleShadowInfo_t &info = m_ShadowsInView[i];
if( m_bDepthTextureActive )
info.m_hShadow = clientShadowHandle;
{
m_ShadowsInView[i].m_flArea = ComputeScreenArea( vecAbsCenter, flRadius );
// Shut down the dummy texture
m_DummyColorTexture.Shutdown();


while( m_DepthTextureCache.Count() )
// Har, har. When water is rendering (or any multipass technique),
{
// we may well initially render from a viewpoint which doesn't include this shadow.
m_DepthTextureCache[ m_DepthTextureCache.Count()-1 ].Shutdown();
// 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;
}


m_DepthTextureCacheLocks.Remove( m_DepthTextureCache.Count()-1 );
m_DepthTextureCache.Remove( m_DepthTextureCache.Count()-1 );
}
m_bDepthTextureActive = false;
}
}


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


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


float fr = (float)m_AmbientLightColor.r / 255.0f;
for ( i = 0; i < nCount - 1; ++i )
float fg = (float)m_AmbientLightColor.g / 255.0f;
{
float fb = (float)m_AmbientLightColor.b / 255.0f;
int nLargestInd = i;
m_RenderShadow->ColorModulate( fr, fg, fb );
float flLargestArea = m_ShadowsInView[m_PriorityIndex[i]].m_flArea;
m_RenderModelShadow->ColorModulate( fr, fg, fb );
for ( j = i + 1; j < nCount; ++j )
 
// 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];
int nIndex = m_PriorityIndex[j];
if ( shadow.m_Flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE )
if ( flLargestArea < m_ShadowsInView[nIndex].m_flArea )
{
{
SetupRenderToTextureShadow( i );
nLargestInd = j;
MarkRenderToTextureShadowDirty( i );
flLargestArea = m_ShadowsInView[nIndex].m_flArea;
 
// Switch the material to use render-to-texture shadows
shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_RenderShadow, m_RenderModelShadow, (void*)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 )
{
{
if (m_RenderToTextureActive)
VPROF_BUDGET( "CVisibleShadowList::FindShadows", VPROF_BUDGETGROUP_SHADOW_RENDERING );
{
// 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
m_ShadowsInView.RemoveAll();
ClientShadow_t& shadow = m_Shadows[i];
ClientLeafSystem()->EnumerateShadowsInLeaves( nLeafCount, pLeafList, this );
shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_SimpleShadow, m_SimpleShadow, (void*)CLIENTSHADOW_INVALID_HANDLE );
int nCount = m_ShadowsInView.Count();
shadowmgr->SetShadowTexCoord( shadow.m_ShadowHandle, 0, 0, 1, 1 );
if (nCount != 0)
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;
}
}




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


// Hook the shadow color into the shadow materials
//-----------------------------------------------------------------------------
m_SimpleShadow->ColorModulate( fr, fg, fb );
// 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 (m_RenderToTextureActive)
if ( args.ArgC() == 4 )
{
{
m_RenderShadow->ColorModulate( fr, fg, fb );
dir.x = atof( args[1] );
m_RenderModelShadow->ColorModulate( fr, fg, fb );
dir.y = atof( args[2] );
dir.z = atof( args[3] );
s_ClientShadowMgr.SetShadowDirection(dir);
}
}
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
CON_COMMAND_F( r_shadowangles, "Set shadow angles", FCVAR_CHEAT )
{
{
*r = m_AmbientLightColor.r;
Vector dir;
*g = m_AmbientLightColor.g;
QAngle angles;
*b = m_AmbientLightColor.b;
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)
//-----------------------------------------------------------------------------
// 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();
angles.x = atof( args[1] );
m_bRenderTargetNeedsClear = true;
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 )
//-----------------------------------------------------------------------------
// Clean up all shadows
//-----------------------------------------------------------------------------
void CClientShadowMgr::LevelShutdownPostEntity()
{
{
// All shadows *should* have been cleaned up when the entities went away
if (args.ArgC() == 1)
// but, just in case....
{
Assert( m_Shadows.Count() == 0 );
unsigned char r, g, b;
s_ClientShadowMgr.GetShadowColor( &r, &g, &b );
Msg( "Shadow color %d %d %d\n", r, g, b );
return;
}


ClientShadowHandle_t h = m_Shadows.Head();
if (args.ArgC() == 4)
while (h != CLIENTSHADOW_INVALID_HANDLE)
{
{
ClientShadowHandle_t next = m_Shadows.Next(h);
int r = atoi( args[1] );
DestroyShadow( h );
int g = atoi( args[2] );
h = next;
int b = atoi( args[3] );
s_ClientShadowMgr.SetShadowColor(r, g, b);
}
}
}


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


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


 
CON_COMMAND_F( r_shadowblobbycutoff, "some shadow stuff", FCVAR_CHEAT )
//-----------------------------------------------------------------------------
// Deals with alt-tab
//-----------------------------------------------------------------------------
void CClientShadowMgr::RestoreRenderState()
{
{
// Mark all shadows dirty; they need to regenerate their state
if (args.ArgC() == 1)
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;
float flArea = s_ClientShadowMgr.GetBlobbyCutoffArea( );
Msg( "Cutoff area %.2f\n", flArea );
return;
}
}


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


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


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Does all the lovely stuff we need to do to have render-to-texture shadows
// Initialization, shutdown
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetupRenderToTextureShadow( ClientShadowHandle_t h )
bool CClientShadowMgr::Init()
{
{
// First, compute how much texture memory we want to use.
m_bRenderTargetNeedsClear = false;
ClientShadow_t& shadow = m_Shadows[h];
m_SimpleShadow.Init( "decals/simpleshadow", TEXTURE_GROUP_DECAL );
 
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
Vector dir( 0.1, 0.1, -1 );
if ( !pRenderable )
SetShadowDirection(dir);
return;
SetShadowDistance( 50 );
 
SetShadowBlobbyCutoffArea( 0.005 );


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


// Compute the maximum dimension
bool bLowEnd = ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 );
Vector size;
VectorSubtract( maxs, mins, size );
float maxSize = max( size.x, size.y );
maxSize = max( maxSize, size.z );


// Figure out the texture size
if ( !bLowEnd && r_shadowrendertotexture.GetBool() )
// 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;
InitRenderToTextureShadows();
}
}


shadow.m_ShadowTexture = m_ShadowAllocator.AllocateTexture( textureSize, textureSize );
// 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() )
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 );
InitDepthTextureShadows();
shadow.m_ShadowTexture = INVALID_TEXTURE_HANDLE;
}
}
materials->AddRestoreFunc( ShadowRestoreFunc );
return true;
}
}


 
void CClientShadowMgr::Shutdown()
//-----------------------------------------------------------------------------
// 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) )
m_SimpleShadow.Shutdown();
{
m_Shadows.RemoveAll();
ClientShadow_t& shadow = m_Shadows[i];
ShutdownRenderToTextureShadows();


// Don't bother with flashlights
ShutdownDepthTextureShadows();
if ( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) != 0 )
continue;


IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
materials->RemoveRestoreFunc( ShadowRestoreFunc );
if ( !pRenderable )
continue;
 
Assert( pRenderable->GetShadowHandle() == i );
AddToDirtyShadowList( pRenderable, true );
}
}
}




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


if ( m_RenderToTextureActive )
if( !m_bDepthTextureActive )
{
{
UpdateAllShadows();
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();


const Vector &CClientShadowMgr::GetShadowDirection() const
#if defined( _X360 )
{
// For the 360, we'll be rendering depth directly into the dummy depth and Resolve()ing to the depth texture.
// This will cause blobby shadows to always project straight down
// only need the dummy surface, don't care about color results
static Vector s_vecDown( 0, 0, -1 );
m_DummyColorTexture.InitRenderTargetTexture( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), RT_SIZE_OFFSCREEN, IMAGE_FORMAT_BGR565, MATERIAL_RT_DEPTH_SHARED, false, "_rt_ShadowDummy" );
if ( !m_RenderToTextureActive )
m_DummyColorTexture.InitRenderTargetSurface( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), IMAGE_FORMAT_BGR565, true );
return s_vecDown;
#else
m_DummyColorTexture.InitRenderTarget( r_flashlightdepthres.GetInt(), r_flashlightdepthres.GetInt(), RT_SIZE_OFFSCREEN, nullFormat, MATERIAL_RT_DEPTH_NONE, false, "_rt_ShadowDummy" );
#endif


return m_SimpleShadowDir;
// 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 )
// Gets shadow information for a particular renderable
// create a render target to use as a resolve target to get the shared depth buffer
//-----------------------------------------------------------------------------
// surface is effectively never used
float CClientShadowMgr::GetShadowDistance( IClientRenderable *pRenderable ) const
depthTex.InitRenderTargetTexture( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_OFFSCREEN, dstFormat, MATERIAL_RT_DEPTH_NONE, false, strRTName );
{
depthTex.InitRenderTargetSurface( 1, 1, dstFormat, false );
float flDist = m_flShadowCastDist;
#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 );
}


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


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


const Vector &CClientShadowMgr::GetShadowDirection( IClientRenderable *pRenderable ) const
void CClientShadowMgr::ShutdownDepthTextureShadows()  
{
{
Vector &vecResult = AllocTempVector();
if( m_bDepthTextureActive )
vecResult = GetShadowDirection();
{
// Shut down the dummy texture
m_DummyColorTexture.Shutdown();


// Allow the renderable to override the default
while( m_DepthTextureCache.Count() )
pRenderable->GetShadowCastDirection( &vecResult, GetActualShadowCastType( pRenderable ) );
{
m_DepthTextureCache[ m_DepthTextureCache.Count()-1 ].Shutdown();


return vecResult;
m_DepthTextureCacheLocks.Remove( m_DepthTextureCache.Count()-1 );
m_DepthTextureCache.Remove( m_DepthTextureCache.Count()-1 );
}
 
m_bDepthTextureActive = false;
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Sets the shadow distance
// Initialize, shutdown render-to-texture shadows
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetShadowDistance( float flMaxDistance )
void CClientShadowMgr::InitRenderToTextureShadows()
{
{
m_flShadowCastDist = flMaxDistance;
if ( !m_RenderToTextureActive )
UpdateAllShadows();
{
}
m_RenderToTextureActive = true;
m_RenderShadow.Init( "decals/rendershadow", TEXTURE_GROUP_DECAL );
m_RenderModelShadow.Init( "decals/rendermodelshadow", TEXTURE_GROUP_DECAL );
m_ShadowAllocator.Init();


float CClientShadowMgr::GetShadowDistance( ) const
m_ShadowAllocator.Reset();
{
m_bRenderTargetNeedsClear = true;
return m_flShadowCastDist;
}


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
// Sets the screen area at which blobby shadows are always used
for (ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
//-----------------------------------------------------------------------------
{
void CClientShadowMgr::SetShadowBlobbyCutoffArea( float flMinArea )
ClientShadow_t& shadow = m_Shadows[i];
{
if ( shadow.m_Flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE )
m_flMinShadowArea = flMinArea;
{
}
SetupRenderToTextureShadow( i );
MarkRenderToTextureShadowDirty( i );


float CClientShadowMgr::GetBlobbyCutoffArea( ) const
// Switch the material to use render-to-texture shadows
{
shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_RenderShadow, m_RenderModelShadow, (void*)(uintp)i );
return m_flMinShadowArea;
}
}
}
}
}


//-----------------------------------------------------------------------------
void CClientShadowMgr::ShutdownRenderToTextureShadows()
// Purpose:
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetFalloffBias( ClientShadowHandle_t handle, unsigned char ucBias )
{
{
shadowmgr->SetFalloffBias( m_Shadows[handle].m_ShadowHandle, ucBias );
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;
}
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Returns the shadow texture
// Sets the shadow color
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
ITexture* CClientShadowMgr::GetShadowTexture( unsigned short h )
void CClientShadowMgr::SetShadowColor( unsigned char r, unsigned char g, unsigned char b )
{
{
return m_ShadowAllocator.GetTexture();
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
//-----------------------------------------------------------------------------
// Returns information needed by the model proxy
//-----------------------------------------------------------------------------
const ShadowInfo_t& CClientShadowMgr::GetShadowInfo( ClientShadowHandle_t h )
{
{
return shadowmgr->GetInfo( m_Shadows[h].m_ShadowHandle );
*r = m_AmbientLightColor.r;
*g = m_AmbientLightColor.g;
*b = m_AmbientLightColor.b;
}
}




//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Renders the shadow texture to screen...
// Level init... get the shadow color
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::RenderShadowTexture( int w, int h )
void CClientShadowMgr::LevelInitPreEntity()
{
{
if (m_RenderToTextureActive)
m_bUpdatingDirtyShadows = false;
{
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->Bind( m_RenderShadow );
IMesh* pMesh = pRenderContext->GetDynamicMesh( true );


CMeshBuilder meshBuilder;
Vector ambientColor;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
engine->GetAmbientLightColor( ambientColor );
ambientColor *= 3;
ambientColor += Vector( 0.3f, 0.3f, 0.3f );


meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
unsigned char r = ambientColor[0] > 1.0 ? 255 : 255 * ambientColor[0];
meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
unsigned char g = ambientColor[1] > 1.0 ? 255 : 255 * ambientColor[1];
meshBuilder.Color4ub( 0, 0, 0, 0 );
unsigned char b = ambientColor[2] > 1.0 ? 255 : 255 * ambientColor[2];
meshBuilder.AdvanceVertex();


meshBuilder.Position3f( w, 0.0f, 0.0f );
SetShadowColor(r, g, b);
meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
meshBuilder.Color4ub( 0, 0, 0, 0 );
meshBuilder.AdvanceVertex();


meshBuilder.Position3f( w, h, 0.0f );
// Set up the texture allocator
meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
if ( m_RenderToTextureActive )
meshBuilder.Color4ub( 0, 0, 0, 0 );
{
meshBuilder.AdvanceVertex();
m_ShadowAllocator.Reset();
 
m_bRenderTargetNeedsClear = true;
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();
}
}
}
}
Line 1,818: Line 1,839:


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Create/destroy a shadow
// Clean up all shadows
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
ClientShadowHandle_t CClientShadowMgr::CreateProjectedTexture( ClientEntityHandle_t entity, int flags )
void CClientShadowMgr::LevelShutdownPostEntity()
{
{
// We need to know if it's a brush model for shadows
// All shadows *should* have been cleaned up when the entities went away
if( !( flags & SHADOW_FLAGS_FLASHLIGHT ) )
// but, just in case....
Assert( m_Shadows.Count() == 0 );
 
ClientShadowHandle_t h = m_Shadows.Head();
while (h != CLIENTSHADOW_INVALID_HANDLE)
{
{
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( entity );
ClientShadowHandle_t next = m_Shadows.Next(h);
int modelType = modelinfo->GetModelType( pRenderable->GetModel() );
DestroyShadow( h );
if (modelType == mod_brush)
h = next;
{
}
flags |= SHADOW_FLAGS_BRUSH_MODEL;
 
}
// Deallocate all textures
if (m_RenderToTextureActive)
{
m_ShadowAllocator.DeallocateAllTextures();
}
}


ClientShadowHandle_t h = m_Shadows.AddToTail();
r_shadows_gamecontrol.SetValue( -1 );
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) )
//-----------------------------------------------------------------------------
// 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) )
{
{
SetupRenderToTextureShadow(h);
m_Shadows[h].m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;
 
pShadowMaterial = m_RenderShadow;
pShadowModelMaterial = m_RenderModelShadow;
pShadowProxyData = (void*)h;
}
}


if( flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE )
SetShadowColor( m_AmbientLightColor.r, m_AmbientLightColor.g, m_AmbientLightColor.b );
{
m_bRenderTargetNeedsClear = true;
pShadowMaterial = m_RenderShadow;
}
pShadowModelMaterial = m_RenderModelShadow;
pShadowProxyData = (void*)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 )
//-----------------------------------------------------------------------------
// Does all the lovely stuff we need to do to have render-to-texture shadows
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetupRenderToTextureShadow( ClientShadowHandle_t h )
{
{
// We don't really need a model entity handle for a projective light source, so use an invalid one.
// First, compute how much texture memory we want to use.
static ClientEntityHandle_t invalidHandle = INVALID_CLIENTENTITY_HANDLE;
ClientShadow_t& shadow = m_Shadows[h];
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
if ( !pRenderable )
return;


int shadowFlags = SHADOW_FLAGS_FLASHLIGHT | SHADOW_FLAGS_LIGHT_WORLD;
Vector mins, maxs;
if( lightState.m_bEnableShadows && r_flashlightdepthtexture.GetBool() )
pRenderable->GetShadowRenderBounds( mins, maxs, GetActualShadowCastType( h ) );
{
shadowFlags |= SHADOW_FLAGS_USE_DEPTH_TEXTURE;
}


ClientShadowHandle_t shadowHandle = CreateProjectedTexture( invalidHandle, shadowFlags );
// Compute the maximum dimension
Vector size;
VectorSubtract( maxs, mins, size );
float maxSize = MAX( size.x, size.y );
maxSize = MAX( maxSize, size.z );


UpdateFlashlightState( shadowHandle, lightState );
// Figure out the texture size
UpdateProjectedTexture( shadowHandle, true );
// For now, we're going to assume a fixed number of shadow texels
return shadowHandle;
// 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 );
}
}
 
ClientShadowHandle_t CClientShadowMgr::CreateShadow( ClientEntityHandle_t entity, int flags )
 
void CClientShadowMgr::CleanUpRenderToTextureShadow( ClientShadowHandle_t h )
{
{
// We don't really need a model entity handle for a projective light source, so use an invalid one.
ClientShadow_t& shadow = m_Shadows[h];
flags &= ~SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK;
if (m_RenderToTextureActive && (shadow.m_Flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE))
flags |= SHADOW_FLAGS_SHADOW | SHADOW_FLAGS_TEXTURE_DIRTY;
ClientShadowHandle_t shadowHandle = CreateProjectedTexture( entity, flags );
 
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( entity );
if ( pRenderable )
{
{
Assert( !pRenderable->IsShadowDirty( ) );
m_ShadowAllocator.DeallocateTexture( shadow.m_ShadowTexture );
pRenderable->MarkShadowDirty( true );
shadow.m_ShadowTexture = INVALID_TEXTURE_HANDLE;
}
}
// 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
// Causes all shadows to be re-updated
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateFlashlightState( ClientShadowHandle_t shadowHandle, const FlashlightState_t &flashlightState )
void CClientShadowMgr::UpdateAllShadows()
{
{
VPROF_BUDGET( "CClientShadowMgr::UpdateFlashlightState", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
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;


BuildPerspectiveWorldToFlashlightMatrix( m_Shadows[shadowHandle].m_WorldToShadow, flashlightState );
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
if ( !pRenderable )
shadowmgr->UpdateFlashlightState( m_Shadows[shadowHandle].m_ShadowHandle, flashlightState );
continue;
 
Assert( pRenderable->GetShadowHandle() == i );
AddToDirtyShadowList( pRenderable, true );
}
}
}


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


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Remove a shadow from the dirty list
// Sets the shadow direction
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::RemoveShadowFromDirtyList( ClientShadowHandle_t handle )
void CClientShadowMgr::SetShadowDirection( const Vector& dir )
{
{
int idx = m_DirtyShadows.Find( handle );
VectorCopy( dir, m_SimpleShadowDir );
if ( idx != m_DirtyShadows.InvalidIndex() )
VectorNormalize( m_SimpleShadowDir );
 
if ( m_RenderToTextureActive )
{
{
// Clean up the shadow update bit.
UpdateAllShadows();
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[handle].m_Entity );
if ( pRenderable )
{
pRenderable->MarkShadowDirty( false );
}
m_DirtyShadows.RemoveAt( idx );
}
}
}
}


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;
// 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
// Gets shadow information for a particular renderable
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::BuildGeneralWorldToShadowMatrix( VMatrix& matWorldToShadow,
float CClientShadowMgr::GetShadowDistance( IClientRenderable *pRenderable ) const
const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec )
{
{
// We're assuming here that xvec + yvec aren't necessary perpendicular
float flDist = m_flShadowCastDist;


// The shadow->world matrix is pretty simple:
// Allow the renderable to override the default
// Just stick the origin in the translation component
pRenderable->GetShadowCastDistance( &flDist, GetActualShadowCastType( pRenderable ) );
// 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
return flDist;
MatrixInverseGeneral( matWorldToShadow, matWorldToShadow );
}
}


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


matrix3x4_t matOrientation;
// Allow the renderable to override the default
QuaternionMatrix( quatOrientation, matOrientation ); // Convert quat to matrix3x4
pRenderable->GetShadowCastDirection( &vecResult, GetActualShadowCastType( pRenderable ) );
PositionMatrix( vec3_origin, matOrientation ); // Zero out translation elements


VMatrix matBasis( matOrientation ); // Convert matrix3x4 to VMatrix
return vecResult;
}


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 );
// Sets the shadow distance
 
//-----------------------------------------------------------------------------
translation *= -1.0f;
void CClientShadowMgr::SetShadowDistance( float flMaxDistance )
matWorldToShadow.SetTranslation( translation );
{
 
m_flShadowCastDist = flMaxDistance;
// The the bottom row.
UpdateAllShadows();
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 )
float CClientShadowMgr::GetShadowDistance( ) const
{
{
VPROF_BUDGET( "CClientShadowMgr::BuildPerspectiveWorldToFlashlightMatrix", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
return m_flShadowCastDist;
 
}
// 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
// Sets the screen area at which blobby shadows are always used
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
float CClientShadowMgr::ComputeLocalShadowOrigin( IClientRenderable* pRenderable,
void CClientShadowMgr::SetShadowBlobbyCutoffArea( float flMinArea )
const Vector& mins, const Vector& maxs, const Vector& localShadowDir, float backupFactor, Vector& origin )
{
{
// Compute the centroid of the object...
m_flMinShadowArea = flMinArea;
Vector vecCentroid;
}
VectorAdd( mins, maxs, vecCentroid );
vecCentroid *= 0.5f;


Vector vecSize;
float CClientShadowMgr::GetBlobbyCutoffArea( ) const
VectorSubtract( maxs, mins, vecSize );
{
float flRadius = vecSize.Length() * 0.5f;
return m_flMinShadowArea;
}


// 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,
// Purpose:  
// and the point on that line corresponds to the endpoint of the box that is
//-----------------------------------------------------------------------------
// furthest *back* along the shadow direction
void CClientShadowMgr::SetFalloffBias( ClientShadowHandle_t handle, unsigned char ucBias )
{
shadowmgr->SetFalloffBias( m_Shadows[handle].m_ShadowHandle, ucBias );
}


// 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
// Returns the shadow texture
// 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
ITexture* CClientShadowMgr::GetShadowTexture( unsigned short h )
{
return m_ShadowAllocator.GetTexture();
}


// 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
// Returns information needed by the model proxy
// of that with the localShadowDir. lastly, we're subtracting out the
//-----------------------------------------------------------------------------
// centroid projection to give us a distance along the localShadowDir to
const ShadowInfo_t& CClientShadowMgr::GetShadowInfo( ClientShadowHandle_t h )
// the front and back of the cube along the direction of the ray.
{
float centroidProjection = DotProduct( vecCentroid, localShadowDir );
return shadowmgr->GetInfo( m_Shadows[h].m_ShadowHandle );
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
// Renders the shadow texture to screen...
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static inline void SortAbsVectorComponents( const Vector& src, int* pVecIdx )
void CClientShadowMgr::RenderShadowTexture( int w, int h )
{
{
Vector absVec( fabs(src[0]), fabs(src[1]), fabs(src[2]) );
if (m_RenderToTextureActive)
 
int maxIdx = (absVec[0] > absVec[1]) ? 0 : 1;
if (absVec[2] > absVec[maxIdx])
{
{
maxIdx = 2;
CMatRenderContextPtr pRenderContext( materials );
}
pRenderContext->Bind( m_RenderShadow );
IMesh* pMesh = pRenderContext->GetDynamicMesh( true );


// always choose something right-handed....
CMeshBuilder meshBuilder;
switch( maxIdx )
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
{
 
case 0:
meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
pVecIdx[0] = 1;
meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
pVecIdx[1] = 2;
meshBuilder.Color4ub( 0, 0, 0, 0 );
pVecIdx[2] = 0;
meshBuilder.AdvanceVertex();
break;
 
case 1:
meshBuilder.Position3f( w, 0.0f, 0.0f );
pVecIdx[0] = 2;
meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
pVecIdx[1] = 0;
meshBuilder.Color4ub( 0, 0, 0, 0 );
pVecIdx[2] = 1;
meshBuilder.AdvanceVertex();
break;
 
case 2:
meshBuilder.Position3f( w, h, 0.0f );
pVecIdx[0] = 0;
meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
pVecIdx[1] = 1;
meshBuilder.Color4ub( 0, 0, 0, 0 );
pVecIdx[2] = 2;
meshBuilder.AdvanceVertex();
break;
 
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();
}
}
}
}
Line 2,132: Line 2,098:


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Build the worldtotexture matrix
// Create/destroy a shadow
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void BuildWorldToTextureMatrix( const VMatrix& matWorldToShadow,  
ClientShadowHandle_t CClientShadowMgr::CreateProjectedTexture( ClientEntityHandle_t entity, int flags )
const Vector2D& size, VMatrix& matWorldToTexture )
{
{
// Build a matrix that maps from shadow space to (u,v) coordinates
// We need to know if it's a brush model for shadows
VMatrix shadowToUnit;
if( !( flags & SHADOW_FLAGS_FLASHLIGHT ) )
MatrixBuildScale( shadowToUnit, 1.0f / size.x, 1.0f / size.y, 1.0f );
{
shadowToUnit[0][3] = shadowToUnit[1][3] = 0.5f;
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( entity );
if ( !pRenderable )
return m_Shadows.InvalidIndex();


// Store off the world to (u,v) transformation
int modelType = modelinfo->GetModelType( pRenderable->GetModel() );
MatrixMultiply( shadowToUnit, matWorldToShadow, matWorldToTexture );
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;


static void BuildOrthoWorldToShadowMatrix( VMatrix& worldToShadow,
if ( m_RenderToTextureActive && (flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE) )
const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec )
{
{
SetupRenderToTextureShadow(h);
// 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:
pShadowMaterial = m_RenderShadow;
// Just stick the origin in the translation component
pShadowModelMaterial = m_RenderModelShadow;
// and the vectors in the columns...
pShadowProxyData = (void*)(uintp)h;
// 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;
if( flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE )
Vector3DMultiply( worldToShadow, origin, translation );
{
pShadowMaterial = m_RenderShadow;
pShadowModelMaterial = m_RenderModelShadow;
pShadowProxyData = (void*)(uintp)h;
}


translation *= -1.0f;
int createShadowFlags;
worldToShadow.SetTranslation( translation );
if( flags & SHADOW_FLAGS_FLASHLIGHT )
 
{
// The the bottom row.
// don't use SHADOW_CACHE_VERTS with projective lightsources since we expect that they will change every frame.
worldToShadow[3][0] = worldToShadow[3][1] = worldToShadow[3][2] = 0.0f;
// FIXME: might want to make it cache optionally if it's an entity light that is static.
worldToShadow[3][3] = 1.0f;
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;
// Set extra clip planes related to shadows...
if( lightState.m_bEnableShadows && r_flashlightdepthtexture.GetBool() )
//-----------------------------------------------------------------------------
{
void CClientShadowMgr::ClearExtraClipPlanes( ClientShadowHandle_t h )
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 )
{
{
shadowmgr->ClearExtraClipPlanes( m_Shadows[h].m_ShadowHandle );
// 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 );
}


void CClientShadowMgr::AddExtraClipPlane( ClientShadowHandle_t h, const Vector& normal, float dist )
// 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
shadowmgr->AddExtraClipPlane( m_Shadows[h].m_ShadowHandle, normal, dist );
AddToDirtyShadowList( shadowHandle, true );
return shadowHandle;
}
}




//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Compute the extra shadow planes
// Updates the flashlight direction and re-computes surfaces it should lie on
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeExtraClipPlanes( IClientRenderable* pRenderable,
void CClientShadowMgr::UpdateFlashlightState( ClientShadowHandle_t shadowHandle, const FlashlightState_t &flashlightState )
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
VPROF_BUDGET( "CClientShadowMgr::UpdateFlashlightState", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
// that's got the highest dotproduct with the local shadow dir...
Vector origin = pRenderable->GetRenderOrigin( );
float dir[3];


int i;
BuildPerspectiveWorldToFlashlightMatrix( m_Shadows[shadowHandle].m_WorldToShadow, flashlightState );
for ( i = 0; i < 3; ++i )
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() )
{
{
if (localShadowDir[i] < 0.0f)
// Clean up the shadow update bit.
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[handle].m_Entity );
if ( pRenderable )
{
{
VectorMA( origin, maxs[i], vec[i], origin );
pRenderable->MarkShadowDirty( false );
dir[i] = 1;
}
else
{
VectorMA( origin, mins[i], vec[i], origin );
dir[i] = -1;
}
}
m_DirtyShadows.RemoveAt( idx );
}
}
}


// 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 );
// Remove a shadow
if ( pEntity && pEntity->m_bEnableRenderingClipPlane )
//-----------------------------------------------------------------------------
{
void CClientShadowMgr::DestroyShadow( ClientShadowHandle_t handle )
normal[ 0 ] = -pEntity->m_fRenderingClipPlane[ 0 ];
{
normal[ 1 ] = -pEntity->m_fRenderingClipPlane[ 1 ];
Assert( m_Shadows.IsValidIndex(handle) );
normal[ 2 ] = -pEntity->m_fRenderingClipPlane[ 2 ];
RemoveShadowFromDirtyList( handle );
AddExtraClipPlane( handle, normal, -pEntity->m_fRenderingClipPlane[ 3 ] - 0.5f );
shadowmgr->DestroyShadow( m_Shadows[handle].m_ShadowHandle );
}
ClientLeafSystem()->RemoveShadow( m_Shadows[handle].m_ClientLeafShadowHandle );
CleanUpRenderToTextureShadow( handle );
m_Shadows.Remove(handle);
}
}




inline ShadowType_t CClientShadowMgr::GetActualShadowCastType( ClientShadowHandle_t handle ) const
//-----------------------------------------------------------------------------
// Build the worldtotexture matrix
//-----------------------------------------------------------------------------
void CClientShadowMgr::BuildGeneralWorldToShadowMatrix( VMatrix& matWorldToShadow,
const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec )
{
{
if ( handle == CLIENTSHADOW_INVALID_HANDLE )
// We're assuming here that xvec + yvec aren't necessary perpendicular
{
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
// The shadow->world matrix is pretty simple:
{
// Just stick the origin in the translation component
return GetActualShadowCastType( pEnt->GetShadowHandle() );
// 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 )
//-----------------------------------------------------------------------------
// Adds a shadow to all leaves along a ray
//-----------------------------------------------------------------------------
class CShadowLeafEnum : public ISpatialLeafEnumerator
{
{
public:
// The shadow->world matrix is pretty simple:
bool EnumerateLeaf( int leaf, int context )
// Just stick the origin in the translation component
{
// and the vectors in the columns...
m_LeafList.AddToTail( leaf );
// The inverse of this transposes the rotational component
return true;
// and the translational component =  - (rotation transpose) * origin
}


CUtlVectorFixedGrowable< int, 512 > m_LeafList;
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;
// Builds a list of leaves inside the shadow volume
matBasis.GetBasisVectors( vForward, vLeft, vUp );
//-----------------------------------------------------------------------------
matBasis.SetForward( vLeft ); // Bizarre vector flip inherited from earlier code, WTF?
static void BuildShadowLeafList( CShadowLeafEnum *pEnum, const Vector& origin,  
matBasis.SetLeft( vUp );
const Vector& dir, const Vector2D& size, float maxDist )
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 )
{
{
Ray_t ray;
VPROF_BUDGET( "CClientShadowMgr::BuildPerspectiveWorldToFlashlightMatrix", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
VectorCopy( origin, ray.m_Start );
 
VectorMultiply( dir, maxDist, ray.m_Delta );
// Buildworld to shadow matrix, then perspective projection and concatenate
ray.m_StartOffset.Init( 0, 0, 0 );
VMatrix matWorldToShadowView, matPerspective;
BuildWorldToShadowMatrix( matWorldToShadowView, flashlightState.m_vecLightOrigin,
  flashlightState.m_quatOrientation );


float flRadius = sqrt( size.x * size.x + size.y * size.y ) * 0.5f;
MatrixBuildPerspective( matPerspective, flashlightState.m_fHorizontalFOVDegrees,
ray.m_Extents.Init( flRadius, flRadius, flRadius );
flashlightState.m_fVerticalFOVDegrees,
ray.m_IsRay = false;
flashlightState.m_NearZ, flashlightState.m_FarZ );
ray.m_IsSwept = true;


ISpatialQuery* pQuery = engine->GetBSPTreeQuery();
MatrixMultiply( matPerspective, matWorldToShadowView, matWorldToShadow );
pQuery->EnumerateLeavesAlongRay( ray, pEnum, 0 );
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Builds a simple blobby shadow
// Compute the shadow origin and attenuation start distance
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::BuildOrthoShadow( IClientRenderable* pRenderable,  
float CClientShadowMgr::ComputeLocalShadowOrigin( IClientRenderable* pRenderable,  
ClientShadowHandle_t handle, 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 vecShadowDir = GetShadowDirection( handle );
Vector vecSize;
VectorSubtract( maxs, mins, vecSize );
float flRadius = vecSize.Length() * 0.5f;


// Project the shadow casting direction into the space of the object
// NOTE: The *origin* of the shadow cast is a point on a line passing through
Vector localShadowDir;
// the centroid of the caster. The direction of this line is the shadow cast direction,
localShadowDir[0] = DotProduct( vec[0], vecShadowDir );
// and the point on that line corresponds to the endpoint of the box that is
localShadowDir[1] = DotProduct( vec[1], vecShadowDir );
// furthest *back* along the shadow direction
localShadowDir[2] = DotProduct( vec[2], vecShadowDir );


// Figure out which vector has the largest component perpendicular
// For the first point at which the shadow could possibly start falling off,
// to the shadow handle...
// we need to use the point at which the ray described above leaves the
// Sort by how perpendicular it is
// bounding sphere surrounding the entity. This is necessary because otherwise,
int vecIdx[3];
// tall, thin objects would have their shadows appear + disappear as then spun about their origin
SortAbsVectorComponents( localShadowDir, vecIdx );


// Here's our shadow basis vectors; namely the ones that are
// Figure out the corner corresponding to the min + max projection
// most perpendicular to the shadow casting direction
// along the shadow direction
Vector xvec = vec[vecIdx[0]];
Vector yvec = vec[vecIdx[1]];


// Project them into a plane perpendicular to the shadow direction
// We're basically finding the point on the cube that has the largest and smallest
xvec -= vecShadowDir * DotProduct( vecShadowDir, xvec );
// dot product with the local shadow dir. Then we're taking the dot product
yvec -= vecShadowDir * DotProduct( vecShadowDir, yvec );
// of that with the localShadowDir. lastly, we're subtracting out the
VectorNormalize( xvec );
// centroid projection to give us a distance along the localShadowDir to
VectorNormalize( yvec );
// 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];
}
}


// Compute the box size
minDist *= backupFactor;
Vector boxSize;
VectorSubtract( maxs, mins, boxSize );


// We project the two longest sides into the vectors perpendicular
VectorMA( vecCentroid, minDist, localShadowDir, origin );
// 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
return flRadius - minDist;
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 );
// 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]) );


// Place the origin at the point with min dot product with shadow dir
int maxIdx = (absVec[0] > absVec[1]) ? 0 : 1;
Vector org;
if (absVec[2] > absVec[maxIdx])
float falloffStart = ComputeLocalShadowOrigin( pRenderable, mins, maxs, localShadowDir, 2.0f, org );
{
maxIdx = 2;
}


// Transform the local origin into world coordinates
// always choose something right-handed....
Vector worldOrigin = pRenderable->GetRenderOrigin( );
switch( maxIdx )
VectorMA( worldOrigin, org.x, vec[0], worldOrigin );
{
VectorMA( worldOrigin, org.y, vec[1], worldOrigin );
case 0:
VectorMA( worldOrigin, org.z, vec[2], worldOrigin );
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;
}
}


// 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;
// Build the worldtotexture matrix
BuildGeneralWorldToShadowMatrix( m_Shadows[handle].m_WorldToShadow, worldOrigin, vecShadowDir, xvec, yvec );
//-----------------------------------------------------------------------------
BuildWorldToTextureMatrix( m_Shadows[handle].m_WorldToShadow, size, matWorldToTexture );
static void BuildWorldToTextureMatrix( const VMatrix& matWorldToShadow,  
Vector2DCopy( size, m_Shadows[handle].m_WorldSize );
const Vector2D& size, VMatrix& matWorldToTexture )
{
// Compute the falloff attenuation
// Build a matrix that maps from shadow space to (u,v) coordinates
// Area computation isn't exact since xvec is not perp to yvec, but close enough
VMatrix shadowToUnit;
// float shadowArea = size.x * size.y;
MatrixBuildScale( shadowToUnit, 1.0f / size.x, 1.0f / size.y, 1.0f );
shadowToUnit[0][3] = shadowToUnit[1][3] = 0.5f;


// The entity may be overriding our shadow cast distance
// Store off the world to (u,v) transformation
float flShadowCastDistance = GetShadowDistance( pRenderable );
MatrixMultiply( shadowToUnit, matWorldToShadow, matWorldToTexture );
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
static void BuildOrthoWorldToShadowMatrix( VMatrix& worldToShadow,
// FIXME!!!!!!!!!!!!!!  Removing this for now since it seems to mess up the blobby shadows.
const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec )
// ComputeExtraClipPlanes( pEnt, handle, vec, mins, maxs, localShadowDir );
{
// 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 );


// Add the shadow to the client leaf system so it correctly marks
// The shadow->world matrix is pretty simple:
// leafs as being affected by a particular shadow
// Just stick the origin in the translation component
ClientLeafSystem()->ProjectShadow( m_Shadows[handle].m_ClientLeafShadowHandle, nCount, pLeafList );
// 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;
// Visualization....
worldToShadow.SetTranslation( translation );
//-----------------------------------------------------------------------------
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;
// The the bottom row.
VectorSubtract( maxs, mins, vecSize );
worldToShadow[3][0] = worldToShadow[3][1] = worldToShadow[3][2] = 0.0f;
worldToShadow[3][3] = 1.0f;
}


Vector vecOrigin = pRenderable->GetRenderOrigin();
Vector start, end, end2;


VectorMA( vecOrigin, mins.x, vec[0], start );
//-----------------------------------------------------------------------------
VectorMA( start, mins.y, vec[1], start );
// Set extra clip planes related to shadows...
VectorMA( start, mins.z, vec[2], start );
//-----------------------------------------------------------------------------
void CClientShadowMgr::ClearExtraClipPlanes( ClientShadowHandle_t h )
{
shadowmgr->ClearExtraClipPlanes( m_Shadows[h].m_ShadowHandle );
}


VectorMA( start, vecSize.x, vec[0], end );
void CClientShadowMgr::AddExtraClipPlane( ClientShadowHandle_t h, const Vector& normal, float dist )
VectorMA( end, vecSize.z, vec[2], end2 );
{
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  
shadowmgr->AddExtraClipPlane( m_Shadows[h].m_ShadowHandle, normal, dist );
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 );
// Compute the extra shadow planes
//-----------------------------------------------------------------------------
start = end;
void CClientShadowMgr::ComputeExtraClipPlanes( IClientRenderable* pRenderable,  
VectorMA( start, vecSize.x, vec[0], end );
ClientShadowHandle_t handle, const Vector* vec,  
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  
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];


VectorMA( start, vecSize.y, vec[1], end );
int i;
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  
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;
}
}


VectorMA( end, vecSize.x, vec[0], start );
// Now that we have it, create 3 planes...
VectorMA( start, -vecSize.x, vec[0], end );
Vector normal;
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  
ClearExtraClipPlanes(handle);
for ( i = 0; i < 3; ++i )
{
VectorMultiply( vec[i], dir[i], normal );
float dist = DotProduct( normal, origin );
AddExtraClipPlane( handle, normal, dist );
}


VectorMA( start, -vecSize.y, vec[1], end );
ClientShadow_t& shadow = m_Shadows[handle];
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  
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 );
}
}


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


start = end;
inline ShadowType_t CClientShadowMgr::GetActualShadowCastType( ClientShadowHandle_t handle ) const
VectorMA( start, -vecSize.x, vec[0], end );
{
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );
if ( handle == CLIENTSHADOW_INVALID_HANDLE )
 
{
VectorMA( start, -vecSize.y, vec[1], end );
return SHADOWS_NONE;
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  
}
 
C_BaseEntity *pEnt = pRenderable->GetIClientUnknown()->GetBaseEntity();
if ( m_Shadows[handle].m_Flags & SHADOW_FLAGS_USE_RENDER_TO_TEXTURE )
if ( pEnt )
{
return ( m_RenderToTextureActive ? SHADOWS_RENDER_TO_TEXTURE : SHADOWS_SIMPLE );
}
else if( m_Shadows[handle].m_Flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE )
{
{
debugoverlay->AddTextOverlay( vecOrigin, 0, "%d", pEnt->entindex() );
return SHADOWS_RENDER_TO_DEPTH_TEXTURE;
}
}
else
else
{
{
debugoverlay->AddTextOverlay( vecOrigin, 0, "%X", (size_t)pRenderable );
return SHADOWS_SIMPLE;
}
}
}
}


inline ShadowType_t CClientShadowMgr::GetActualShadowCastType( IClientRenderable *pEnt ) const
{
return GetActualShadowCastType( pEnt->GetShadowHandle() );
}


extern ConVar cl_drawshadowtexture;
extern ConVar cl_shadowtextureoverlaysize;


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Builds a more complex shadow...
// Adds a shadow to all leaves along a ray
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::BuildRenderToTextureShadow( IClientRenderable* pRenderable,
class CShadowLeafEnum : public ISpatialLeafEnumerator
ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs)
{
{
if ( cl_drawshadowtexture.GetInt() )
public:
bool EnumerateLeaf( int leaf, int context )
{
{
// Red wireframe bounding box around objects whose RTT shadows are being updated that frame
m_LeafList.AddToTail( leaf );
DrawRenderToTextureDebugInfo( pRenderable, mins, maxs );
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
// Get the object's basis
Vector vec[3];
Vector vec[3];
Line 2,499: Line 2,599:
Vector vecShadowDir = GetShadowDirection( handle );
Vector vecShadowDir = GetShadowDirection( handle );


// Debugging aid
// Project the shadow casting direction into the space of the object
// const model_t *pModel = pRenderable->GetModel();
Vector localShadowDir;
// 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[0] = DotProduct( vec[0], vecShadowDir );
localShadowDir[1] = DotProduct( vec[1], vecShadowDir );
localShadowDir[1] = DotProduct( vec[1], vecShadowDir );
localShadowDir[2] = DotProduct( vec[2], 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
// Compute the box size
Vector boxSize;
Vector boxSize;
VectorSubtract( maxs, mins, 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
// We project the two longest sides into the vectors perpendicular
// to the projection direction, then add in the projection of the perp direction
// to the projection direction, then add in the projection of the perp direction
Vector2D size;
Vector2D size( boxSize[vecIdx[0]], boxSize[vecIdx[1]] );
size.x = boxSize.x * fabs( DotProduct( vec[0], xvec ) ) +
size.x *= fabs( DotProduct( vec[vecIdx[0]], xvec ) );
boxSize.y * fabs( DotProduct( vec[1], xvec ) ) +  
size.y *= fabs( DotProduct( vec[vecIdx[1]], yvec ) );
boxSize.z * fabs( DotProduct( vec[2], xvec ) );
 
size.y = boxSize.x * fabs( DotProduct( vec[0], yvec ) ) +  
// Add the third component into x and y
boxSize.y * fabs( DotProduct( vec[1], yvec ) ) +  
size.x += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], xvec ) );
boxSize.z * fabs( DotProduct( vec[2], yvec ) );
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;


size.x += 2.0f * TEXEL_SIZE_PER_CASTER_SIZE;
// Clamp the minimum size
size.y += 2.0f * TEXEL_SIZE_PER_CASTER_SIZE;
Vector2DMax( size, Vector2D(10.0f, 10.0f), size );


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


// Transform the local origin into world coordinates
// Transform the local origin into world coordinates
Line 2,556: Line 2,653:
VectorMA( worldOrigin, org.z, vec[2], worldOrigin );
VectorMA( worldOrigin, org.z, vec[2], worldOrigin );


VMatrix matWorldToTexture;
// FUNKY: A trick to reduce annoying texelization artifacts!?
BuildOrthoWorldToShadowMatrix( m_Shadows[handle].m_WorldToShadow, worldOrigin, vecShadowDir, xvec, yvec );
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 );
BuildWorldToTextureMatrix( m_Shadows[handle].m_WorldToShadow, size, matWorldToTexture );
Vector2DCopy( size, m_Shadows[handle].m_WorldSize );
Vector2DCopy( size, m_Shadows[handle].m_WorldSize );
 
// Compute the falloff attenuation
// Compute the falloff attenuation
// Area computation isn't exact since xvec is not perp to yvec, but close enough
// 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;
// float shadowArea = size.x * size.y;


Line 2,575: Line 2,678:
const int *pLeafList = leafList.m_LeafList.Base();
const int *pLeafList = leafList.m_LeafList.Base();


shadowmgr->ProjectShadow( m_Shadows[handle].m_ShadowHandle, worldOrigin,  
shadowmgr->ProjectShadow( m_Shadows[handle].m_ShadowHandle, worldOrigin,
vecShadowDir, matWorldToTexture, size, nCount, pLeafList, maxHeight, falloffStart, MAX_FALLOFF_AMOUNT, pRenderable->GetRenderOrigin() );
vecShadowDir, matWorldToTexture, size, nCount, pLeafList, maxHeight, falloffStart, MAX_FALLOFF_AMOUNT, pRenderable->GetRenderOrigin() );


// Compute extra clip planes to prevent poke-thru
// Compute extra clip planes to prevent poke-thru
ComputeExtraClipPlanes( pRenderable, handle, vec, mins, maxs, localShadowDir );
// 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  
// Add the shadow to the client leaf system so it correctly marks  
Line 2,586: Line 2,690:
}
}


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 );
// 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;


static void DebugDrawFrustum( const Vector &vOrigin, const VMatrix &matWorldToFlashlight )
VectorMA( vecOrigin, mins.x, vec[0], start );
{
VectorMA( start, mins.y, vec[1], start );
VMatrix flashlightToWorld;
VectorMA( start, mins.z, vec[2], start );
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
VectorMA( start, vecSize.x, vec[0], end );
LineDrawHelper( Vector( 0.5f, 0.5f, 0.0f ), Vector( 1.0f, 0.5f, 0.0f ), flashlightToWorld, 255,   0,   0 );
VectorMA( end, vecSize.z, vec[2], end2 );
LineDrawHelper( Vector( 0.5f, 0.5f, 0.0f ), Vector( 0.5f, 1.0f, 0.0f ), flashlightToWorld,   0, 255,   0 );
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  
LineDrawHelper( Vector( 0.5f, 0.5f, 0.0f ), Vector( 0.5f, 0.5f, 0.35f ), flashlightToWorld,   0,   0, 255 );
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 );
// Builds a list of leaves inside the flashlight volume
VectorMA( start, -vecSize.x, vec[0], end );
//-----------------------------------------------------------------------------
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  
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 );
}


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


void CClientShadowMgr::BuildFlashlight( ClientShadowHandle_t handle )
VectorMA( start, -vecSize.z, vec[2], end );
{
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );
// 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 );
start = end;
VectorMA( start, -vecSize.x, vec[0], end );
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  


bool bLightModels = r_flashlightmodels.GetBool();
VectorMA( start, -vecSize.y, vec[1], end );
bool bLightSpecificEntity = shadow.m_hTargetEntity.Get() != NULL;
debugoverlay->AddLineOverlay( start, end, 255, 0, 0, true, 0.01 );  
bool bLightWorld = ( shadow.m_Flags & SHADOW_FLAGS_LIGHT_WORLD ) != 0;
int nCount = 0;
const int *pLeafList = 0;


CShadowLeafEnum leafList;
C_BaseEntity *pEnt = pRenderable->GetIClientUnknown()->GetBaseEntity();
if ( bLightWorld || ( bLightModels && !bLightSpecificEntity ) )
if ( pEnt )
{
{
BuildFlashlightLeafList( &leafList, shadow.m_WorldToShadow );
debugoverlay->AddTextOverlay( vecOrigin, 0, "%d", pEnt->entindex() );
nCount = leafList.m_LeafList.Count();
pLeafList = leafList.m_LeafList.Base();
}
 
if( bLightWorld )
{
shadowmgr->ProjectFlashlight( shadow.m_ShadowHandle, shadow.m_WorldToShadow, nCount, pLeafList );
}
}
else
else
{
{
// This should clear all models and surfaces from this shadow
debugoverlay->AddTextOverlay( vecOrigin, 0, "%X", (size_t)pRenderable );
shadowmgr->EnableShadow( shadow.m_ShadowHandle, false );
shadowmgr->EnableShadow( shadow.m_ShadowHandle, true );
}
}
}


if ( !bLightModels )
extern ConVar cl_drawshadowtexture;
return;
extern ConVar cl_shadowtextureoverlaysize;


if ( !bLightSpecificEntity )
//-----------------------------------------------------------------------------
{
// Builds a more complex shadow...
// Add the shadow to the client leaf system so it correctly marks
//-----------------------------------------------------------------------------
// leafs as being affected by a particular shadow
void CClientShadowMgr::BuildRenderToTextureShadow( IClientRenderable* pRenderable,
ClientLeafSystem()->ProjectFlashlight( shadow.m_ClientLeafShadowHandle, nCount, pLeafList );
ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs)
return;
{
if ( cl_drawshadowtexture.GetInt() )
{
// Red wireframe bounding box around objects whose RTT shadows are being updated that frame
DrawRenderToTextureDebugInfo( pRenderable, mins, maxs );
}
}


// We know what we are focused on, so just add the shadow directly to that receiver
// Get the object's basis
Assert( shadow.m_hTargetEntity->GetModel() );
Vector vec[3];
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
vec[1] *= -1.0f;


C_BaseEntity *pChild = shadow.m_hTargetEntity->FirstMoveChild();
Vector vecShadowDir = GetShadowDirection( handle );
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();
// Debugging aid
}
// const model_t *pModel = pRenderable->GetModel();
// const char *pDebugName = modelinfo->GetModelName( pModel );


int modelType = modelinfo->GetModelType( shadow.m_hTargetEntity->GetModel() );
// Project the shadow casting direction into the space of the object
if (modelType == mod_brush)
Vector localShadowDir;
{
localShadowDir[0] = DotProduct( vec[0], vecShadowDir );
AddShadowToReceiver( handle, shadow.m_hTargetEntity, SHADOW_RECEIVER_BRUSH_MODEL );
localShadowDir[1] = DotProduct( vec[1], vecShadowDir );
}
localShadowDir[2] = DotProduct( vec[2], vecShadowDir );
else if ( modelType == mod_studio )
{
AddShadowToReceiver( handle, shadow.m_hTargetEntity, SHADOW_RECEIVER_STUDIO_MODEL );
}
}


 
// Compute the box size
//-----------------------------------------------------------------------------
Vector boxSize;
// Adds the child bounds to the bounding box
VectorSubtract( maxs, mins, boxSize );
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddChildBounds( matrix3x4_t &matWorldToBBox, IClientRenderable* pParent, Vector &vecMins, Vector &vecMaxs )
Vector yvec;
{
float fProjMax = 0.0f;
Vector vecChildMins, vecChildMaxs;
for( int i = 0; i != 3; ++i )
Vector vecNewChildMins, vecNewChildMaxs;
matrix3x4_t childToBBox;
 
IClientRenderable *pChild = pParent->FirstShadowChild();
while( pChild )
{
{
// Transform the child bbox into the space of the main bbox
Vector test = vec[i] - ( vecShadowDir * DotProduct( vecShadowDir, vec[i] ) );
// FIXME: Optimize this?
test *= boxSize[i]; //doing after the projection to simplify projection math
if ( GetActualShadowCastType( pChild ) != SHADOWS_NONE)
float fLengthSqr = test.LengthSqr();
if( fLengthSqr > fProjMax )
{
{
pChild->GetShadowRenderBounds( vecChildMins, vecChildMaxs, SHADOWS_RENDER_TO_TEXTURE );
fProjMax = fLengthSqr;
ConcatTransforms( matWorldToBBox, pChild->RenderableToWorldTransform(), childToBBox );
yvec = test;
TransformAABB( childToBBox, vecChildMins, vecChildMaxs, vecNewChildMins, vecNewChildMaxs );
VectorMin( vecMins, vecNewChildMins, vecMins );
VectorMax( vecMaxs, vecNewChildMaxs, vecMaxs );
}
}
}


AddChildBounds( matWorldToBBox, pChild, vecMins, vecMaxs );
VectorNormalize( yvec );
pChild = pChild->NextShadowPeer();
 
}
// 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;
// Compute a bounds for the entity + children
size.y += 2.0f * TEXEL_SIZE_PER_CASTER_SIZE;
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeHierarchicalBounds( IClientRenderable *pRenderable, Vector &vecMins, Vector &vecMaxs )
{
ShadowType_t shadowType = GetActualShadowCastType( pRenderable );


pRenderable->GetShadowRenderBounds( vecMins, vecMaxs, shadowType );
// 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 );


// We could use a good solution for this in the regular PC build, since
// Transform the local origin into world coordinates
// it causes lots of extra bone setups for entities you can't see.
Vector worldOrigin = pRenderable->GetRenderOrigin( );
if ( IsPC() )
VectorMA( worldOrigin, org.x, vec[0], worldOrigin );
{
VectorMA( worldOrigin, org.y, vec[1], worldOrigin );
IClientRenderable *pChild = pRenderable->FirstShadowChild();
VectorMA( worldOrigin, org.z, vec[2], worldOrigin );


// Don't recurse down the tree when we hit a blobby shadow
VMatrix matWorldToTexture;
if ( pChild && shadowType != SHADOWS_SIMPLE )
BuildOrthoWorldToShadowMatrix( m_Shadows[handle].m_WorldToShadow, worldOrigin, vecShadowDir, xvec, yvec );
{
BuildWorldToTextureMatrix( m_Shadows[handle].m_WorldToShadow, size, matWorldToTexture );
matrix3x4_t matWorldToBBox;
Vector2DCopy( size, m_Shadows[handle].m_WorldSize );
MatrixInvert( pRenderable->RenderableToWorldTransform(), matWorldToBBox );
AddChildBounds( matWorldToBBox, pRenderable, vecMins, vecMaxs );
}
}
}


// 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
// Shadow update functions
float flShadowCastDistance = GetShadowDistance( pRenderable );
//-----------------------------------------------------------------------------
float maxHeight = flShadowCastDistance + falloffStart; //3.0f * sqrt( shadowArea );
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 );
CShadowLeafEnum leafList;
if ( shadowType != SHADOWS_RENDER_TO_TEXTURE )
BuildShadowLeafList( &leafList, worldOrigin, vecShadowDir, size, maxHeight );
{
int nCount = leafList.m_LeafList.Count();
BuildOrthoShadow( pRenderable, handle, mins, maxs );
const int *pLeafList = leafList.m_LeafList.Base();
}
else
{
BuildRenderToTextureShadow( pRenderable, handle, mins, maxs );
}
}
else
{
BuildFlashlight( handle );
}
}


void CClientShadowMgr::UpdateBrushShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle )
shadowmgr->ProjectShadow( m_Shadows[handle].m_ShadowHandle, worldOrigin,
{
vecShadowDir, matWorldToTexture, size, nCount, pLeafList, maxHeight, falloffStart, MAX_FALLOFF_AMOUNT, pRenderable->GetRenderOrigin() );
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 );
// Compute extra clip planes to prevent poke-thru
if ( shadowType != SHADOWS_RENDER_TO_TEXTURE )
ComputeExtraClipPlanes( pRenderable, handle, vec, mins, maxs, localShadowDir );
{
BuildOrthoShadow( pRenderable, handle, mins, maxs );
}
else
{
BuildRenderToTextureShadow( pRenderable, handle, mins, maxs );
}
}
else
{
VPROF_BUDGET( "CClientShadowMgr::UpdateBrushShadow", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );


BuildFlashlight( handle );
// 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 );


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


static bool s_bBreak = false;
static void DebugDrawFrustum( const Vector &vOrigin, const VMatrix &matWorldToFlashlight )
 
void ShadowBreak_f()
{
{
s_bBreak = true;
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 );


static ConCommand r_shadowbreak("r_shadowbreak", ShadowBreak_f);
// 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 );
#endif // _DEBUG
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 );
 
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?
// Builds a list of leaves inside the flashlight volume
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CClientShadowMgr::ShouldUseParentShadow( IClientRenderable *pRenderable )
static void BuildFlashlightLeafList( CShadowLeafEnum *pEnum, const VMatrix &worldToShadow )
{
{
if ( !pRenderable )
// Use an AABB around the frustum to enumerate leaves.
return false;
Vector mins, maxs;
 
CalculateAABBFromProjectionMatrix( worldToShadow, &mins, &maxs );
IClientRenderable *pShadowParent = pRenderable->GetShadowParent();
ISpatialQuery* pQuery = engine->GetBSPTreeQuery();
if ( !pShadowParent )
pQuery->EnumerateLeavesInBox( mins, maxs, pEnum, 0 );
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;
}
}




//-----------------------------------------------------------------------------
void CClientShadowMgr::BuildFlashlight( ClientShadowHandle_t handle )
// 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 );
// For the 360, we just draw flashlights with the main geometry
MDLCACHE_CRITICAL_SECTION();
// 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 );
// -- Shadow Depth Textures -----------------------
//


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 ) )
{
{
// VPROF scope
BuildFlashlightLeafList( &leafList, shadow.m_WorldToShadow );
VPROF_BUDGET( "CClientShadowMgr::PreRender", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
nCount = leafList.m_LeafList.Count();
pLeafList = leafList.m_LeafList.Base();
}


// If someone turned shadow depth mapping on but we can't do it, force it off
if( bLightWorld )
if ( r_flashlightdepthtexture.GetBool() && !materials->SupportsShadowDepthTextures() )
{
{
shadowmgr->ProjectFlashlight( shadow.m_ShadowHandle, shadow.m_WorldToShadow, nCount, pLeafList );
r_flashlightdepthtexture.SetValue( 0 );
}
ShutdownDepthTextureShadows();
else
}
{
// This should clear all models and surfaces from this shadow
shadowmgr->EnableShadow( shadow.m_ShadowHandle, false );
shadowmgr->EnableShadow( shadow.m_ShadowHandle, true );
}


bool bDepthTextureActive    = r_flashlightdepthtexture.GetBool();
if ( !bLightModels )
int  nDepthTextureResolution = r_flashlightdepthres.GetInt();
return;


// If shadow depth texture size or enable/disable changed, do appropriate deallocation/(re)allocation
if ( !bLightSpecificEntity )
if ( ( bDepthTextureActive != m_bDepthTextureActive ) || ( nDepthTextureResolution != m_nDepthTextureResolution ) )
{
{
// Add the shadow to the client leaf system so it correctly marks
// If shadow depth texturing remains on, but resolution changed, shut down and reinitialize depth textures
// leafs as being affected by a particular shadow
if ( ( bDepthTextureActive == true ) && ( m_bDepthTextureActive == true ) &&
ClientLeafSystem()->ProjectFlashlight( shadow.m_ClientLeafShadowHandle, nCount, pLeafList );
( nDepthTextureResolution != m_nDepthTextureResolution ) )
return;
{
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 know what we are focused on, so just add the shadow directly to that receiver
// -- Render to Texture Shadows -----------------------
Assert( shadow.m_hTargetEntity->GetModel() );
//


bool bRenderToTextureActive = r_shadowrendertotexture.GetBool();
C_BaseEntity *pChild = shadow.m_hTargetEntity->FirstMoveChild();
if ( bRenderToTextureActive != m_RenderToTextureActive )
while( pChild )
{
{
if ( m_RenderToTextureActive )
int modelType = modelinfo->GetModelType( pChild->GetModel() );
if (modelType == mod_brush)
{
{
ShutdownRenderToTextureShadows();
AddShadowToReceiver( handle, pChild, SHADOW_RECEIVER_BRUSH_MODEL );
}
}
else
else if ( modelType == mod_studio )
{
{
InitRenderToTextureShadows();
AddShadowToReceiver( handle, pChild, SHADOW_RECEIVER_STUDIO_MODEL );
}
}


UpdateAllShadows();
pChild = pChild->NextMovePeer();
return;
}
}


m_bUpdatingDirtyShadows = true;
int modelType = modelinfo->GetModelType( shadow.m_hTargetEntity->GetModel() );
 
if (modelType == mod_brush)
unsigned short i = m_DirtyShadows.FirstInorder();
while ( i != m_DirtyShadows.InvalidIndex() )
{
{
MDLCACHE_CRITICAL_SECTION();
AddShadowToReceiver( handle, shadow.m_hTargetEntity, SHADOW_RECEIVER_BRUSH_MODEL );
ClientShadowHandle_t& handle = m_DirtyShadows[ i ];
UpdateDirtyShadow(handle);
i = m_DirtyShadows.NextInorder(i);
}
}
m_DirtyShadows.RemoveAll();
else if ( modelType == mod_studio )
 
// 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] );
AddShadowToReceiver( handle, shadow.m_hTargetEntity, SHADOW_RECEIVER_STUDIO_MODEL );
}
}
m_TransparentShadows.RemoveAll();
m_bUpdatingDirtyShadows = false;
}
}




//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Gets the entity whose shadow this shadow will render into
// Adds the child bounds to the bounding box
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
IClientRenderable *CClientShadowMgr::GetParentShadowEntity( ClientShadowHandle_t handle )
void CClientShadowMgr::AddChildBounds( matrix3x4_t &matWorldToBBox, IClientRenderable* pParent, Vector &vecMins, Vector &vecMaxs )
{
{
ClientShadow_t& shadow = m_Shadows[handle];
Vector vecChildMins, vecChildMaxs;
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
Vector vecNewChildMins, vecNewChildMaxs;
if ( pRenderable )
matrix3x4_t childToBBox;
 
IClientRenderable *pChild = pParent->FirstShadowChild();
while( pChild )
{
{
if ( ShouldUseParentShadow( pRenderable ) )
// Transform the child bbox into the space of the main bbox
// FIXME: Optimize this?
if ( GetActualShadowCastType( pChild ) != SHADOWS_NONE)
{
{
IClientRenderable *pParent = pRenderable->GetShadowParent();
pChild->GetShadowRenderBounds( vecChildMins, vecChildMaxs, SHADOWS_RENDER_TO_TEXTURE );
while ( GetActualShadowCastType( pParent ) == SHADOWS_NONE )
ConcatTransforms( matWorldToBBox, pChild->RenderableToWorldTransform(), childToBBox );
{
TransformAABB( childToBBox, vecChildMins, vecChildMaxs, vecNewChildMins, vecNewChildMaxs );
pParent = pParent->GetShadowParent();
VectorMin( vecMins, vecNewChildMins, vecMins );
Assert( pParent );
VectorMax( vecMaxs, vecNewChildMaxs, vecMaxs );
}
return pParent;
}
}
AddChildBounds( matWorldToBBox, pChild, vecMins, vecMaxs );
pChild = pChild->NextShadowPeer();
}
}
return NULL;
}
}




//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Marks a shadow as needing re-projection
// Compute a bounds for the entity + children
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddToDirtyShadowList( ClientShadowHandle_t handle, bool bForce )
void CClientShadowMgr::ComputeHierarchicalBounds( IClientRenderable *pRenderable, Vector &vecMins, Vector &vecMaxs )
{
{
// Don't add to the dirty shadow list while we're iterating over it
ShadowType_t shadowType = GetActualShadowCastType( pRenderable );
// 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 )
pRenderable->GetShadowRenderBounds( vecMins, vecMaxs, shadowType );
return;


Assert( m_DirtyShadows.Find( handle ) == m_DirtyShadows.InvalidIndex() );
// We could use a good solution for this in the regular PC build, since
m_DirtyShadows.Insert( handle );
// it causes lots of extra bone setups for entities you can't see.
 
if ( IsPC() )
// This pretty much guarantees we'll recompute the shadow
if ( bForce )
{
{
m_Shadows[handle].m_LastAngles.Init( FLT_MAX, FLT_MAX, FLT_MAX );
IClientRenderable *pChild = pRenderable->FirstShadowChild();
}


// If we use our parent shadow, then it's dirty too...
// Don't recurse down the tree when we hit a blobby shadow
IClientRenderable *pParent = GetParentShadowEntity( handle );
if ( pChild && shadowType != SHADOWS_SIMPLE )
if ( pParent )
{
{
matrix3x4_t matWorldToBBox;
AddToDirtyShadowList( pParent, bForce );
MatrixInvert( pRenderable->RenderableToWorldTransform(), matWorldToBBox );
AddChildBounds( matWorldToBBox, pRenderable, vecMins, vecMaxs );
}
}
}
}
}
Line 3,035: Line 3,057:


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Marks a shadow as needing re-projection
// Shadow update functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddToDirtyShadowList( IClientRenderable *pRenderable, bool bForce )
void CClientShadowMgr::UpdateStudioShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle )
{
{
// Don't add to the dirty shadow list while we're iterating over it
if( !( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) )
// 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.
Vector mins, maxs;
if ( m_bUpdatingDirtyShadows )
ComputeHierarchicalBounds( pRenderable, mins, maxs );
return;


// Are we already in the dirty list?
ShadowType_t shadowType = GetActualShadowCastType( handle );
if ( pRenderable->IsShadowDirty( ) )
if ( shadowType != SHADOWS_RENDER_TO_TEXTURE )
return;
{
 
BuildOrthoShadow( pRenderable, handle, mins, maxs );
ClientShadowHandle_t handle = pRenderable->GetShadowHandle();
}
if ( handle == CLIENTSHADOW_INVALID_HANDLE )
else
return;
{
 
BuildRenderToTextureShadow( pRenderable, handle, mins, maxs );
#ifdef _DEBUG
}
// Make sure everything's consistent
}
if ( handle != CLIENTSHADOW_INVALID_HANDLE )
else
{
{
IClientRenderable *pShadowRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[handle].m_Entity );
BuildFlashlight( handle );
Assert( pRenderable == pShadowRenderable );
}
}
#endif
pRenderable->MarkShadowDirty( true );
AddToDirtyShadowList( handle, bForce );
}
}


 
void CClientShadowMgr::UpdateBrushShadow( IClientRenderable *pRenderable, ClientShadowHandle_t handle )
//-----------------------------------------------------------------------------
// Marks the render-to-texture shadow as needing to be re-rendered
//-----------------------------------------------------------------------------
void CClientShadowMgr::MarkRenderToTextureShadowDirty( ClientShadowHandle_t handle )
{
{
// Don't add bogus handles!
if( !( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) )
if (handle != CLIENTSHADOW_INVALID_HANDLE)
{
{
// Mark the shadow has having a dirty renter-to-texture
// Compute the bounding box in the space of the shadow...
ClientShadow_t& shadow = m_Shadows[handle];
Vector mins, maxs;
shadow.m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;
ComputeHierarchicalBounds( pRenderable, mins, maxs );


// If we use our parent shadow, then it's dirty too...
ShadowType_t shadowType = GetActualShadowCastType( handle );
IClientRenderable *pParent = GetParentShadowEntity( handle );
if ( shadowType != SHADOWS_RENDER_TO_TEXTURE )
if ( pParent )
{
BuildOrthoShadow( pRenderable, handle, mins, maxs );
}
else
{
{
ClientShadowHandle_t parentHandle = pParent->GetShadowHandle();
BuildRenderToTextureShadow( pRenderable, handle, mins, maxs );
if ( parentHandle != CLIENTSHADOW_INVALID_HANDLE )
{
m_Shadows[parentHandle].m_Flags |= SHADOW_FLAGS_TEXTURE_DIRTY;
}
}
}
}
else
{
VPROF_BUDGET( "CClientShadowMgr::UpdateBrushShadow", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
BuildFlashlight( handle );
}
}
}
}




//-----------------------------------------------------------------------------
#ifdef _DEBUG
// Update a shadow
 
//-----------------------------------------------------------------------------
static bool s_bBreak = false;
void CClientShadowMgr::UpdateShadow( ClientShadowHandle_t handle, bool force )
 
void ShadowBreak_f()
{
{
ClientShadow_t& shadow = m_Shadows[handle];
s_bBreak = true;
}


// Get the client entity....
static ConCommand r_shadowbreak("r_shadowbreak", ShadowBreak_f);
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
 
#endif // _DEBUG
 
 
bool CClientShadowMgr::WillParentRenderBlobbyShadow( IClientRenderable *pRenderable )
{
if ( !pRenderable )
if ( !pRenderable )
{
return false;
// Retire the shadow if the entity is gone
 
DestroyShadow( handle );
IClientRenderable *pShadowParent = pRenderable->GetShadowParent();
return;
if ( !pShadowParent )
}
return false;


// Don't bother if there's no model on the renderable
// If there's *no* shadow casting type, then we want to see if we can render into its parent
if ( !pRenderable->GetModel() )
ShadowType_t shadowType = GetActualShadowCastType( pShadowParent );
{
if ( shadowType == SHADOWS_NONE )
pRenderable->MarkShadowDirty( false );
return WillParentRenderBlobbyShadow( pShadowParent );
return;
}


// FIXME: NOTE! Because this is called from PreRender, the falloff bias is
return shadowType == SHADOWS_SIMPLE;
// 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 );
//-----------------------------------------------------------------------------
// 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;


// Figure out if the shadow moved...
// Can't render into the parent if the parent is blobby
// Even though we have dirty bits, some entities
ShadowType_t shadowType = GetActualShadowCastType( pShadowParent );
// never clear those dirty bits
if ( shadowType == SHADOWS_SIMPLE )
const Vector& origin = pRenderable->GetRenderOrigin();
return false;
const QAngle& angles = pRenderable->GetRenderAngles();


if (force || (origin != shadow.m_LastOrigin) || (angles != shadow.m_LastAngles) || shadow.m_LightPosLerp < 1.0f)
// If there's *no* shadow casting type, then we want to see if we can render into its parent
{
if ( shadowType == SHADOWS_NONE )
// Store off the new pos/orientation
return ShouldUseParentShadow( pShadowParent );
VectorCopy( origin, shadow.m_LastOrigin );
VectorCopy( angles, shadow.m_LastAngles );


CMatRenderContextPtr pRenderContext( materials );
// Here, the parent uses a render-to-texture shadow
const model_t *pModel = pRenderable->GetModel();
return true;
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
// Before we render any view, make sure all shadows are re-projected vs world
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateProjectedTextureInternal( ClientShadowHandle_t handle, bool force )
void CClientShadowMgr::PreRender()
{
{
ClientShadow_t& shadow = m_Shadows[handle];
VPROF_BUDGET( "CClientShadowMgr::PreRender", VPROF_BUDGETGROUP_SHADOW_RENDERING );
MDLCACHE_CRITICAL_SECTION();
 
//
// -- Shadow Depth Textures -----------------------
//


if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
{
{
VPROF_BUDGET( "CClientShadowMgr::UpdateProjectedTextureInternal", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
// VPROF scope
VPROF_BUDGET( "CClientShadowMgr::PreRender", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );


Assert( ( shadow.m_Flags & SHADOW_FLAGS_SHADOW ) == 0 );
// If someone turned shadow depth mapping on but we can't do it, force it off
ClientShadow_t& shadow = m_Shadows[handle];
if ( r_flashlightdepthtexture.GetBool() && !materials->SupportsShadowDepthTextures() )
 
{
shadowmgr->EnableShadow( shadow.m_ShadowHandle, true );
r_flashlightdepthtexture.SetValue( 0 );
ShutdownDepthTextureShadows();
}


// FIXME: What's the difference between brush and model shadows for light projectors? Answer: nothing.
bool bDepthTextureActive    = r_flashlightdepthtexture.GetBool();
UpdateBrushShadow( NULL, handle );
int  nDepthTextureResolution = r_flashlightdepthres.GetInt();
}
 
else
// If shadow depth texture size or enable/disable changed, do appropriate deallocation/(re)allocation
{
if ( ( bDepthTextureActive != m_bDepthTextureActive ) || ( nDepthTextureResolution != m_nDepthTextureResolution ) )
Assert( shadow.m_Flags & SHADOW_FLAGS_SHADOW );
{
Assert( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 );
// If shadow depth texturing remains on, but resolution changed, shut down and reinitialize depth textures
UpdateShadow( handle, force );
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();
// Update a shadow
if ( bRenderToTextureActive != m_RenderToTextureActive )
//-----------------------------------------------------------------------------
{
void CClientShadowMgr::UpdateProjectedTexture( ClientShadowHandle_t handle, bool force )
if ( m_RenderToTextureActive )
{
{
VPROF_BUDGET( "CClientShadowMgr::UpdateProjectedTexture", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
ShutdownRenderToTextureShadows();
}
else
{
InitRenderToTextureShadows();
}


if ( handle == CLIENTSHADOW_INVALID_HANDLE )
UpdateAllShadows();
return;
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();


// NOTE: This can only work for flashlights, since UpdateProjectedTextureInternal
// Transparent shadows must remain dirty, since they were not re-projected
// depends on the falloff offset to cull shadows.
int nCount = m_TransparentShadows.Count();
ClientShadow_t &shadow = m_Shadows[ handle ];
for ( int i = 0; i < nCount; ++i )
if( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 )
{
{
Warning( "CClientShadowMgr::UpdateProjectedTexture can only be used with flashlights!\n" );
m_DirtyShadows.Insert( m_TransparentShadows[i] );
return;
}
}
m_TransparentShadows.RemoveAll();


UpdateProjectedTextureInternal( handle, force );
m_bUpdatingDirtyShadows = false;
RemoveShadowFromDirtyList( handle );
}
}


 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Computes bounding sphere
// Gets the entity whose shadow this shadow will render into
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeBoundingSphere( IClientRenderable* pRenderable, Vector& origin, float& radius )
IClientRenderable *CClientShadowMgr::GetParentShadowEntity( ClientShadowHandle_t handle )
{
{
Assert( pRenderable );
ClientShadow_t& shadow = m_Shadows[handle];
Vector mins, maxs;
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
pRenderable->GetShadowRenderBounds( mins, maxs, GetActualShadowCastType( pRenderable ) );
if ( pRenderable )
Vector size;
{
VectorSubtract( maxs, mins, size );
if ( ShouldUseParentShadow( pRenderable ) )
radius = size.Length() * 0.5f;
{
 
IClientRenderable *pParent = pRenderable->GetShadowParent();
// Compute centroid (local space)
while ( GetActualShadowCastType( pParent ) == SHADOWS_NONE )
Vector centroid;
{
VectorAdd( mins, maxs, centroid );
pParent = pParent->GetShadowParent();
centroid *= 0.5f;
Assert( pParent );
 
}
// Transform centroid into world space
return pParent;
Vector vec[3];
}
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
}
vec[1] *= -1.0f;
return NULL;
 
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
// Marks a shadow as needing re-projection
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeShadowBBox( IClientRenderable *pRenderable, ClientShadowHandle_t shadowHandle, const Vector &vecAbsCenter, float flRadius, Vector *pAbsMins, Vector *pAbsMaxs )
void CClientShadowMgr::AddToDirtyShadowList( ClientShadowHandle_t handle, bool bForce )
{
{
// This is *really* rough. Basically we simply determine the
// Don't add to the dirty shadow list while we're iterating over it
// maximum shadow casting length and extrude the box by that distance
// 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 );


Vector vecShadowDir = GetShadowDirection( shadowHandle );
// This pretty much guarantees we'll recompute the shadow
for (int i = 0; i < 3; ++i)
if ( bForce )
{
{
float flShadowCastDistance = GetShadowDistance( pRenderable );
m_Shadows[handle].m_LastAngles.Init( FLT_MAX, FLT_MAX, FLT_MAX );
float flDist = flShadowCastDistance * vecShadowDir[i];
}


if (vecShadowDir[i] < 0)
// If we use our parent shadow, then it's dirty too...
{
IClientRenderable *pParent = GetParentShadowEntity( handle );
(*pAbsMins)[i] = vecAbsCenter[i] - flRadius + flDist;
if ( pParent )
(*pAbsMaxs)[i] = vecAbsCenter[i] + flRadius;
{
}
AddToDirtyShadowList( pParent, bForce );
else
{
(*pAbsMins)[i] = vecAbsCenter[i] - flRadius;
(*pAbsMaxs)[i] = vecAbsCenter[i] + flRadius + flDist;
}
}
}
}
}
Line 3,296: Line 3,318:


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Compute a separating axis...
// Marks a shadow as needing re-projection
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CClientShadowMgr::ComputeSeparatingPlane( IClientRenderable* pRend1, IClientRenderable* pRend2, cplane_t* pPlane )
void CClientShadowMgr::AddToDirtyShadowList( IClientRenderable *pRenderable, bool bForce )
{
{
Vector min1, max1, min2, max2;
// Don't add to the dirty shadow list while we're iterating over it
pRend1->GetShadowRenderBounds( min1, max1, GetActualShadowCastType( pRend1 ) );
// The only way this can happen is if a child is being rendered into a parent
pRend2->GetShadowRenderBounds( min2, max2, GetActualShadowCastType( pRend2 ) );
// shadow, and we don't need it to be added to the dirty list in that case.
return ::ComputeSeparatingPlane(  
if ( m_bUpdatingDirtyShadows )
pRend1->GetRenderOrigin(), pRend1->GetRenderAngles(), min1, max1,
return;
pRend2->GetRenderOrigin(), pRend2->GetRenderAngles(), min2, max2,
 
3.0f, pPlane );
// 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 );
}
}




//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Cull shadows based on rough bounding volumes
// Marks the render-to-texture shadow as needing to be re-rendered
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CClientShadowMgr::CullReceiver( ClientShadowHandle_t handle, IClientRenderable* pRenderable,
void CClientShadowMgr::MarkRenderToTextureShadowDirty( ClientShadowHandle_t handle )
IClientRenderable* pSourceRenderable )
{
{
// check flags here instead and assert !pSourceRenderable
// Don't add bogus handles!
if( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT )
if (handle != CLIENTSHADOW_INVALID_HANDLE)
{
{
VPROF_BUDGET( "CClientShadowMgr::CullReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
// 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;
}
}
}
}


Assert( !pSourceRenderable );
const Frustum_t &frustum = shadowmgr->GetFlashlightFrustum( m_Shadows[handle].m_ShadowHandle );


Vector mins, maxs;
//-----------------------------------------------------------------------------
pRenderable->GetRenderBoundsWorldspace( mins, maxs );
// Update a shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateShadow( ClientShadowHandle_t handle, bool force )
{
ClientShadow_t& shadow = m_Shadows[handle];


return R_CullBox( mins, maxs, frustum );
// Get the client entity....
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
if ( !pRenderable )
{
// Retire the shadow if the entity is gone
DestroyShadow( handle );
return;
}
}


Assert( pSourceRenderable );
// Don't bother if there's no model on the renderable
// Compute a bounding sphere for the renderable
if ( !pRenderable->GetModel() )
Vector origin;
{
float radius;
pRenderable->MarkShadowDirty( false );
ComputeBoundingSphere( pRenderable, origin, radius );
return;
}


// Transform the sphere center into the space of the shadow
// FIXME: NOTE! Because this is called from PreRender, the falloff bias is
Vector localOrigin;
// off by a frame. We could move the code in PreRender to occur after world
const ClientShadow_t& shadow = m_Shadows[handle];
// list building is done to fix this issue.
const ShadowInfo_t& info = shadowmgr->GetInfo( shadow.m_ShadowHandle );
// Don't bother with it if the shadow is totally transparent
Vector3DMultiplyPosition( shadow.m_WorldToShadow, origin, localOrigin );
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;
}


// Compute a rough bounding box for the shadow (in shadow space)
#ifdef _DEBUG
Vector shadowMin, shadowMax;
if (s_bBreak)
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;
s_bBreak = false;
 
// the plane normal doesn't need to be normalized...
VectorSubtract( origin, originSource, plane.normal );
}
}
else
#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 ) )
{
{
foundSeparatingPlane = ComputeSeparatingPlane( pRenderable, pSourceRenderable, &plane );
shadowmgr->EnableShadow( shadow.m_ShadowHandle, false );
pRenderable->MarkShadowDirty( false );
return;
}
}


if (foundSeparatingPlane)
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 )
{
{
// Compute which side of the plane the renderable is on..
// Store off the new pos/orientation
Vector vecShadowDir = GetShadowDirection( handle );
VectorCopy( origin, shadow.m_LastOrigin );
float shadowDot = DotProduct( vecShadowDir, plane.normal );
VectorCopy( angles, shadow.m_LastAngles );
float receiverDot = DotProduct( plane.normal, origin );
float sourceDot = DotProduct( plane.normal, originSource );


if (shadowDot > 0.0f)
CMatRenderContextPtr pRenderContext( materials );
const model_t *pModel = pRenderable->GetModel();
MaterialFogMode_t fogMode = pRenderContext->GetFogMode();
pRenderContext->FogMode( MATERIAL_FOG_NONE );
switch( modelinfo->GetModelType( pModel ) )
{
{
if (receiverDot <= sourceDot)
case mod_brush:
{
UpdateBrushShadow( pRenderable, handle );
// Vector dest;
break;
// VectorMA( pSourceRenderable->GetRenderOrigin(), 50, plane.normal, dest );  
 
// debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 255, 0, true, 1.0f );
case mod_studio:
return true;
UpdateStudioShadow( pRenderable, handle );
}
break;
else
 
{
default:
// Vector dest;
// Shouldn't get here if not a brush or studio
// VectorMA( pSourceRenderable->GetRenderOrigin(), 50, plane.normal, dest );  
Assert(0);
// debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 0, 0, true, 1.0f );
break;
}
}
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 );
}
}
}
pRenderContext->FogMode( fogMode );
}
}


// No additional clip planes? ok then it's a valid receiver
// NOTE: We can't do this earlier because pEnt->GetRenderOrigin() can
/*
// provoke a recomputation of render origin, which, for aiments, can cause everything
if (shadow.m_ClipPlaneCount == 0)
// to be marked as dirty. So don't clear the flag until this point.
return false;
pRenderable->MarkShadowDirty( 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];
// Update a shadow
Vector mins, maxs;
//-----------------------------------------------------------------------------
cplane_t plane;
void CClientShadowMgr::UpdateProjectedTextureInternal( ClientShadowHandle_t handle, bool force )
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
{
pRenderable->GetBounds( mins, maxs );
ClientShadow_t& shadow = m_Shadows[handle];


for ( i = 0; i < shadow.m_ClipPlaneCount; ++i)
if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
{
{
// Transform the plane into the space of the receiver
VPROF_BUDGET( "CClientShadowMgr::UpdateProjectedTextureInternal", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
plane.normal.x = DotProduct( vec[0], shadow.m_ClipPlane[i] );
 
plane.normal.y = DotProduct( vec[1], shadow.m_ClipPlane[i] );
Assert( ( shadow.m_Flags & SHADOW_FLAGS_SHADOW ) == 0 );
plane.normal.z = DotProduct( vec[2], shadow.m_ClipPlane[i] );
ClientShadow_t& shadow = m_Shadows[handle];


plane.dist = shadow.m_ClipDist[i] - DotProduct( shadow.m_ClipPlane[i], pRenderable->GetRenderOrigin() );
shadowmgr->EnableShadow( shadow.m_ShadowHandle, true );


// If the box is on the front side of the plane, we're done.
// FIXME: What's the difference between brush and model shadows for light projectors? Answer: nothing.
if (BoxOnPlaneSide2( mins, maxs, &plane, 3.0f ) == 1)
UpdateBrushShadow( NULL, handle );
return true;
}
else
{
Assert( shadow.m_Flags & SHADOW_FLAGS_SHADOW );
Assert( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 );
UpdateShadow( handle, force );
}
}
*/
return false;
}
}




//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// deals with shadows being added to shadow receivers
// Update a shadow
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddShadowToReceiver( ClientShadowHandle_t handle,
void CClientShadowMgr::UpdateProjectedTexture( ClientShadowHandle_t handle, bool force )
IClientRenderable* pRenderable, ShadowReceiver_t type )
{
{
ClientShadow_t &shadow = m_Shadows[handle];
VPROF_BUDGET( "CClientShadowMgr::UpdateProjectedTexture", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );


// Don't add a shadow cast by an object to itself...
if ( handle == CLIENTSHADOW_INVALID_HANDLE )
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;
return;


// Don't bother if this renderable doesn't receive shadows or light from flashlights
// NOTE: This can only work for flashlights, since UpdateProjectedTextureInternal
if( !pRenderable->ShouldReceiveProjectedTextures( SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ) )
// 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;
return;
}


// Cull if the origin is on the wrong side of a shadow clip plane....
UpdateProjectedTextureInternal( handle, force );
if ( CullReceiver( handle, pRenderable, pSourceRenderable ) )
RemoveShadowFromDirtyList( handle );
return;
}


// Do different things depending on the receiver type
switch( type )
//-----------------------------------------------------------------------------
{
// Computes bounding sphere
case SHADOW_RECEIVER_BRUSH_MODEL:
//-----------------------------------------------------------------------------
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;


if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
// Compute centroid (local space)
{
Vector centroid;
VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
VectorAdd( mins, maxs, centroid );
centroid *= 0.5f;


if( (!shadow.m_hTargetEntity) || IsFlashlightTarget( handle, pRenderable ) )
// Transform centroid into world space
{
Vector vec[3];
shadowmgr->AddShadowToBrushModel( shadow.m_ShadowHandle,
AngleVectors( pRenderable->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
const_cast<model_t*>(pRenderable->GetModel()),
vec[1] *= -1.0f;
pRenderable->GetRenderOrigin(), pRenderable->GetRenderAngles() );
 
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];


shadowmgr->AddFlashlightRenderable( shadow.m_ShadowHandle, pRenderable );
if (vecShadowDir[i] < 0)
}
{
(*pAbsMins)[i] = vecAbsCenter[i] - flRadius + flDist;
(*pAbsMaxs)[i] = vecAbsCenter[i] + flRadius;
}
}
else
else
{
{
shadowmgr->AddShadowToBrushModel( shadow.m_ShadowHandle,
(*pAbsMins)[i] = vecAbsCenter[i] - flRadius;
const_cast<model_t*>(pRenderable->GetModel()),
(*pAbsMaxs)[i] = vecAbsCenter[i] + flRadius + flDist;
pRenderable->GetRenderOrigin(), pRenderable->GetRenderAngles() );
}
}
break;
}
}
 


case SHADOW_RECEIVER_STATIC_PROP:
//-----------------------------------------------------------------------------
// Don't add shadows to props if we're not using render-to-texture
// Compute a separating axis...
if ( GetActualShadowCastType( handle ) == SHADOWS_RENDER_TO_TEXTURE )
//-----------------------------------------------------------------------------
{
bool CClientShadowMgr::ComputeSeparatingPlane( IClientRenderable* pRend1, IClientRenderable* pRend2, cplane_t* pPlane )
// Also don't add them unless an NPC or player casts them..
{
// They are wickedly expensive!!!
Vector min1, max1, min2, max2;
C_BaseEntity *pEnt = pSourceRenderable->GetIClientUnknown()->GetBaseEntity();
pRend1->GetShadowRenderBounds( min1, max1, GetActualShadowCastType( pRend1 ) );
if ( pEnt && ( pEnt->GetFlags() & (FL_NPC | FL_CLIENT)) )
pRend2->GetShadowRenderBounds( min2, max2, GetActualShadowCastType( pRend2 ) );
{
return ::ComputeSeparatingPlane(  
staticpropmgr->AddShadowToStaticProp( shadow.m_ShadowHandle, pRenderable );
pRend1->GetRenderOrigin(), pRend1->GetRenderAngles(), min1, max1,
}
pRend2->GetRenderOrigin(), pRend2->GetRenderAngles(), min2, max2,
}
3.0f, pPlane );
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 );
//-----------------------------------------------------------------------------
}
// Cull shadows based on rough bounding volumes
}
//-----------------------------------------------------------------------------
break;
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 );


case SHADOW_RECEIVER_STUDIO_MODEL:
Assert( !pSourceRenderable );
if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
const Frustum_t &frustum = shadowmgr->GetFlashlightFrustum( m_Shadows[handle].m_ShadowHandle );
{
 
VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
Vector mins, maxs;
pRenderable->GetRenderBoundsWorldspace( mins, maxs );


if( (!shadow.m_hTargetEntity) || IsFlashlightTarget( handle, pRenderable ) )
return R_CullBox( mins, maxs, frustum );
{
pRenderable->CreateModelInstance();
shadowmgr->AddShadowToModel( shadow.m_ShadowHandle, pRenderable->GetModelInstance() );
shadowmgr->AddFlashlightRenderable( shadow.m_ShadowHandle, pRenderable );
}
}
break;
// default:
}
}
}


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)
// deals with shadows being added to shadow receivers
Vector shadowMin, shadowMax;
//-----------------------------------------------------------------------------
shadowMin.Init( -shadow.m_WorldSize.x * 0.5f, -shadow.m_WorldSize.y * 0.5f, 0 );
void CClientShadowMgr::RemoveAllShadowsFromReceiver(
shadowMax.Init( shadow.m_WorldSize.x * 0.5f, shadow.m_WorldSize.y * 0.5f, info.m_MaxDist );
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
// If the bounding sphere doesn't intersect with the shadow volume, cull
switch( type )
if (!IsBoxIntersectingSphere( shadowMin, shadowMax, localOrigin, radius ))
{
return true;
case SHADOW_RECEIVER_BRUSH_MODEL:
{
model_t* pModel = const_cast<model_t*>(pRenderable->GetModel());
shadowmgr->RemoveAllShadowsFromBrushModel( pModel );
}
break;


case SHADOW_RECEIVER_STATIC_PROP:
Vector originSource;
staticpropmgr->RemoveAllShadowsFromStaticProp(pRenderable);
float radiusSource;
break;
ComputeBoundingSphere( pSourceRenderable, originSource, radiusSource );


case SHADOW_RECEIVER_STUDIO_MODEL:
// Fast check for separating plane...
if( pRenderable && pRenderable->GetModelInstance() != MODEL_INSTANCE_INVALID )
bool foundSeparatingPlane = false;
{
cplane_t plane;
shadowmgr->RemoveAllShadowsFromModel( pRenderable->GetModelInstance() );
if (!IsSphereIntersectingSphere( originSource, radiusSource, origin, radius ))
}
{
break;
foundSeparatingPlane = true;


// default:
// the plane normal doesn't need to be normalized...
// // FIXME: How do deal with this stuff? Add a method to IClientRenderable?
VectorSubtract( origin, originSource, plane.normal );
// C_BaseEntity* pEnt = static_cast<C_BaseEntity*>(pRenderable);
}
// pEnt->RemoveAllShadows();
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)
// 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);
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
else
{
{
int nModelType = modelinfo->GetModelType( pRenderable->GetModel() );
if (receiverDot >= sourceDot)
bDrawModelShadow = nModelType == mod_studio;
{
}
// Vector dest;
 
// VectorMA( pSourceRenderable->GetRenderOrigin(), -50, plane.normal, dest );
if ( bDrawModelShadow )
// debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 255, 0, true, 1.0f );
{
return true;
C_BaseEntity *pEntity = pRenderable->GetIClientUnknown()->GetBaseEntity();
}
if ( pEntity )
else
{
{
if ( pEntity->IsNPC() )
// Vector dest;
{
// VectorMA( pSourceRenderable->GetRenderOrigin(), 50, plane.normal, dest );  
s_NPCShadowBoneSetups.AddToTail( assert_cast<C_BaseAnimating *>( pEntity ) );
// debugoverlay->AddLineOverlay( pSourceRenderable->GetRenderOrigin(), dest, 255, 0, 0, true, 1.0f );
}
else if ( pEntity->GetBaseAnimating() )
{
s_NonNPCShadowBoneSetups.AddToTail( assert_cast<C_BaseAnimating *>( pEntity ) );
}
 
}
}
bDrewTexture = true;
}
}
}
}


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


IClientRenderable *pChild;
// Check the additional cull planes
for ( pChild = pRenderable->FirstShadowChild(); pChild; pChild = pChild->NextShadowPeer() )
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)
{
{
if ( BuildSetupShadowHierarchy( pChild, shadow, true ) )
// Transform the plane into the space of the receiver
{
plane.normal.x = DotProduct( vec[0], shadow.m_ClipPlane[i] );
bDrewTexture = true;
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 bDrewTexture;
*/
 
return false;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Draws all children shadows into our own
// deals with shadows being added to shadow receivers
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CClientShadowMgr::DrawShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild )
void CClientShadowMgr::AddShadowToReceiver( ClientShadowHandle_t handle,
IClientRenderable* pRenderable, ShadowReceiver_t type )
{
{
bool bDrewTexture = false;
ClientShadow_t &shadow = m_Shadows[handle];


// Stop traversing when we hit a blobby shadow
// Don't add a shadow cast by an object to itself...
ShadowType_t shadowType = GetActualShadowCastType( pRenderable );
IClientRenderable* pSourceRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
if ( pRenderable && shadowType == SHADOWS_SIMPLE )
 
return false;
// 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;


if ( !pRenderable || shadowType != SHADOWS_NONE )
// Do different things depending on the receiver type
switch( type )
{
{
bool bDrawModelShadow;
case SHADOW_RECEIVER_BRUSH_MODEL:
bool bDrawBrushShadow;
 
if ( !bChild )
if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
{
{
bDrawModelShadow = ((shadow.m_Flags & SHADOW_FLAGS_BRUSH_MODEL) == 0);
VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
bDrawBrushShadow = !bDrawModelShadow;
 
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
else
{
{
int nModelType = modelinfo->GetModelType( pRenderable->GetModel() );
shadowmgr->AddShadowToBrushModel( shadow.m_ShadowHandle,
bDrawModelShadow = nModelType == mod_studio;
const_cast<model_t*>(pRenderable->GetModel()),
bDrawBrushShadow = nModelType == mod_brush;
pRenderable->GetRenderOrigin(), pRenderable->GetRenderAngles() );
}
}
   
break;
if ( bDrawModelShadow )
 
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 )
{
{
DrawModelInfo_t info;
// Also don't add them unless an NPC or player casts them..
matrix3x4_t *pBoneToWorld = modelrender->DrawModelShadowSetup( pRenderable, pRenderable->GetBody(), pRenderable->GetSkin(), &info );
// They are wickedly expensive!!!
if ( pBoneToWorld )
C_BaseEntity *pEnt = pSourceRenderable->GetIClientUnknown()->GetBaseEntity();
if ( pEnt && ( pEnt->GetFlags() & (FL_NPC | FL_CLIENT)) )
{
{
modelrender->DrawModelShadow( pRenderable, info, pBoneToWorld );
staticpropmgr->AddShadowToStaticProp( shadow.m_ShadowHandle, pRenderable );
}
}
bDrewTexture = true;
}
}
else if ( bDrawBrushShadow )
else if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
{
{
render->DrawBrushModelShadow( pRenderable );
VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
bDrewTexture = true;
}
}


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


IClientRenderable *pChild;
shadowmgr->AddFlashlightRenderable( shadow.m_ShadowHandle, pRenderable );
for ( pChild = pRenderable->FirstShadowChild(); pChild; pChild = pChild->NextShadowPeer() )
}
{
if ( DrawShadowHierarchy( pChild, shadow, true ) )
{
bDrewTexture = true;
}
}
}
break;
return bDrewTexture;
}


//-----------------------------------------------------------------------------
case SHADOW_RECEIVER_STUDIO_MODEL:
// This gets called with every shadow that potentially will need to re-render
if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
//-----------------------------------------------------------------------------
{
bool CClientShadowMgr::BuildSetupListForRenderToTextureShadow( unsigned short clientShadowHandle, float flArea )
VPROF_BUDGET( "CClientShadowMgr::AddShadowToReceiver", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
{
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 ) )
if( (!shadow.m_hTargetEntity) || IsFlashlightTarget( handle, pRenderable ) )
return false;
{
 
pRenderable->CreateModelInstance();
// shadow to be redrawn; for now, we'll always do it.
shadowmgr->AddShadowToModel( shadow.m_ShadowHandle, pRenderable->GetModelInstance() );
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
shadowmgr->AddFlashlightRenderable( shadow.m_ShadowHandle, pRenderable );
 
}
if ( BuildSetupShadowHierarchy( pRenderable, shadow ) )
}
return true;
break;
// default:
}
}
return false;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// This gets called with every shadow that potentially will need to re-render
// deals with shadows being added to shadow receivers
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CClientShadowMgr::DrawRenderToTextureShadow( unsigned short clientShadowHandle, float flArea )
void CClientShadowMgr::RemoveAllShadowsFromReceiver(  
IClientRenderable* pRenderable, ShadowReceiver_t type )
{
{
ClientShadow_t& shadow = m_Shadows[clientShadowHandle];
// Don't bother if this renderable doesn't receive shadows
if ( !pRenderable->ShouldReceiveProjectedTextures( SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ) )
return;


// If we were previously using the LOD shadow, set the material
// Do different things depending on the receiver type
bool bPreviouslyUsingLODShadow = ( shadow.m_Flags & SHADOW_FLAGS_USING_LOD_SHADOW ) != 0;
switch( type )
shadow.m_Flags &= ~SHADOW_FLAGS_USING_LOD_SHADOW;
if ( bPreviouslyUsingLODShadow )
{
{
shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_RenderShadow, m_RenderModelShadow, (void*)clientShadowHandle );
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;


// Mark texture as being used...
case SHADOW_RECEIVER_STUDIO_MODEL:
bool bDirtyTexture = (shadow.m_Flags & SHADOW_FLAGS_TEXTURE_DIRTY) != 0;
if( pRenderable && pRenderable->GetModelInstance() != MODEL_INSTANCE_INVALID )
bool bDrewTexture = false;
{
bool bNeedsRedraw = ( !m_bThreaded && m_ShadowAllocator.UseTexture( shadow.m_ShadowTexture, bDirtyTexture, flArea ) );
shadowmgr->RemoveAllShadowsFromModel( pRenderable->GetModelInstance() );
}
break;


if ( !m_ShadowAllocator.HasValidTexture( shadow.m_ShadowTexture ) )
// default:
{
// // FIXME: How do deal with this stuff? Add a method to IClientRenderable?
DrawRenderToTextureShadowLOD( clientShadowHandle );
// C_BaseEntity* pEnt = static_cast<C_BaseEntity*>(pRenderable);
return false;
// pEnt->RemoveAllShadows();
}
}
}


if ( bNeedsRedraw || bDirtyTexture )
{
// shadow to be redrawn; for now, we'll always do it.
IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );


CMatRenderContextPtr pRenderContext( materials );
//-----------------------------------------------------------------------------
// Computes + sets the render-to-texture texcoords
// Sets the viewport state
//-----------------------------------------------------------------------------
int x, y, w, h;
void CClientShadowMgr::SetRenderToTextureShadowTexCoords( ShadowHandle_t handle, int x, int y, int w, int h )
m_ShadowAllocator.GetTextureRect( shadow.m_ShadowTexture, x, y, w, h );
{
pRenderContext->Viewport( IsX360() ? 0 : x, IsX360() ? 0 : y, w, 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;


// Clear the selected viewport only (don't need to clear depth)
u  = ((float)x + 0.5f) / (float)textureW;
pRenderContext->ClearBuffers( true, false );
v  = ((float)y + 0.5f) / (float)textureH;
du = ((float)w - 1) / (float)textureW;
dv = ((float)h - 1) / (float)textureH;


pRenderContext->MatrixMode( MATERIAL_VIEW );
shadowmgr->SetShadowTexCoord( handle, u, v, du, dv );
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
// Setup all children shadows
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::DrawRenderToTextureShadowLOD( unsigned short clientShadowHandle )
bool CClientShadowMgr::BuildSetupShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild )
{
{
ClientShadow_t &shadow = m_Shadows[clientShadowHandle];
bool bDrewTexture = false;
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( shadow.m_ShadowHandle );
shadow.m_Flags |= SHADOW_FLAGS_USING_LOD_SHADOW;
}
}


// 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 )
// Advances to the next frame,
{
//-----------------------------------------------------------------------------
bool bDrawModelShadow;
void CClientShadowMgr::AdvanceFrame()
if ( !bChild )
{
{
// We're starting the next frame
bDrawModelShadow = ((shadow.m_Flags & SHADOW_FLAGS_BRUSH_MODEL) == 0);
m_ShadowAllocator.AdvanceFrame();
}
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;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Re-render shadow depth textures that lie in the leaf list
// Draws all children shadows into our own
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CClientShadowMgr::BuildActiveShadowDepthList( const CViewSetup &viewSetup, int nMaxDepthShadows, ClientShadowHandle_t *pActiveDepthShadows )
bool CClientShadowMgr::DrawShadowHierarchy( IClientRenderable *pRenderable, const ClientShadow_t &shadow, bool bChild )
{
{
int nActiveDepthShadowCount = 0;
bool bDrewTexture = false;
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!
// Stop traversing when we hit a blobby shadow
if ( ( shadow.m_Flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE ) == 0 )
ShadowType_t shadowType = GetActualShadowCastType( pRenderable );
continue;
if ( pRenderable && shadowType == SHADOWS_SIMPLE )
return false;


const FlashlightState_t& flashlightState = shadowmgr->GetFlashlightState( shadow.m_ShadowHandle );
if ( !pRenderable || shadowType != SHADOWS_NONE )
 
{
// Bail if this flashlight doesn't want shadows
bool bDrawModelShadow;
if ( !flashlightState.m_bEnableShadows )
bool bDrawBrushShadow;
continue;
if ( !bChild )
 
// 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 );
bDrawModelShadow = ((shadow.m_Flags & SHADOW_FLAGS_BRUSH_MODEL) == 0);
continue;
bDrawBrushShadow = !bDrawModelShadow;
}
}
 
else
if ( nActiveDepthShadowCount >= nMaxDepthShadows )
{
{
static bool s_bOverflowWarning = false;
int nModelType = modelinfo->GetModelType( pRenderable->GetModel() );
if ( !s_bOverflowWarning )
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 )
{
{
Warning( "Too many depth textures rendered in a single view!\n" );
modelrender->DrawModelShadow( pRenderable, info, pBoneToWorld );
Assert( 0 );
s_bOverflowWarning = true;
}
}
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
bDrewTexture = true;
continue;
}
else if ( bDrawBrushShadow )
{
render->DrawBrushModelShadow( pRenderable );
bDrewTexture = true;
}
}
}
if ( !pRenderable )
return bDrewTexture;


pActiveDepthShadows[nActiveDepthShadowCount++] = i;
IClientRenderable *pChild;
for ( pChild = pRenderable->FirstShadowChild(); pChild; pChild = pChild->NextShadowPeer() )
{
if ( DrawShadowHierarchy( pChild, shadow, true ) )
{
bDrewTexture = true;
}
}
}
return nActiveDepthShadowCount;
return bDrewTexture;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Sets the view's active flashlight render state
// This gets called with every shadow that potentially will need to re-render
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetViewFlashlightState( int nActiveFlashlightCount, ClientShadowHandle_t* pActiveFlashlights )
bool CClientShadowMgr::BuildSetupListForRenderToTextureShadow( unsigned short clientShadowHandle, float flArea )
{
{
// NOTE: On the 360, we render the entire scene with the flashlight state
ClientShadow_t& shadow = m_Shadows[clientShadowHandle];
// set and don't render flashlights additively in the shadow mgr at a far later time
bool bDirtyTexture = (shadow.m_Flags & SHADOW_FLAGS_TEXTURE_DIRTY) != 0;
// because the CPU costs are prohibitive
bool bNeedsRedraw = m_ShadowAllocator.UseTexture( shadow.m_ShadowTexture, bDirtyTexture, flArea );
if ( !IsX360() && !r_flashlight_version2.GetInt() )
if ( bNeedsRedraw || bDirtyTexture )
return;
{
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 );


Assert( nActiveFlashlightCount<= 1 );
if ( BuildSetupShadowHierarchy( pRenderable, shadow ) )
if ( nActiveFlashlightCount > 0 )
return true;
{
Assert( ( m_Shadows[ pActiveFlashlights[0] ].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) != 0 );
shadowmgr->SetFlashlightRenderState( pActiveFlashlights[0] );
}
else
{
shadowmgr->SetFlashlightRenderState( SHADOW_HANDLE_INVALID );
}
}
return false;
}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Re-render shadow depth textures that lie in the leaf list
// This gets called with every shadow that potentially will need to re-render
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeShadowDepthTextures( const CViewSetup &viewSetup )
bool CClientShadowMgr::DrawRenderToTextureShadow( unsigned short clientShadowHandle, float flArea )
{
{
VPROF_BUDGET( "CClientShadowMgr::ComputeShadowDepthTextures", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
ClientShadow_t& shadow = m_Shadows[clientShadowHandle];


CMatRenderContextPtr pRenderContext( materials );
// If we were previously using the LOD shadow, set the material
PIXEVENT( pRenderContext, "Shadow Depth Textures" );
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 );
}


// Build list of active render-to-texture shadows
// Mark texture as being used...
ClientShadowHandle_t pActiveDepthShadows[1024];
bool bDirtyTexture = (shadow.m_Flags & SHADOW_FLAGS_TEXTURE_DIRTY) != 0;
int nActiveDepthShadowCount = BuildActiveShadowDepthList( viewSetup, ARRAYSIZE( pActiveDepthShadows ), pActiveDepthShadows );
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;
}


// Iterate over all existing textures and allocate shadow textures
if ( bNeedsRedraw || bDirtyTexture )
bool bDebugFrustum = r_flashlightdrawfrustum.GetBool();
for ( int j = 0; j < nActiveDepthShadowCount; ++j )
{
{
ClientShadow_t& shadow = m_Shadows[ pActiveDepthShadows[j] ];
// 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 );


CTextureReference shadowDepthTexture;
pRenderContext->MatrixMode( MATERIAL_VIEW );
bool bGotShadowDepthTexture = LockShadowDepthTexture( &shadowDepthTexture );
pRenderContext->LoadMatrix( shadowmgr->GetInfo( shadow.m_ShadowHandle ).m_WorldToShadow );
if ( !bGotShadowDepthTexture )
 
if ( DrawShadowHierarchy( pRenderable, shadow ) )
{
{
// If we don't get one, that means we have too many this frame so bind no depth texture
bDrewTexture = true;
static int bitchCount = 0;
if ( IsX360() )
if( bitchCount < 10 )
{
{
Warning( "Too many shadow maps this frame!\n"  );
// resolve render target to system memory texture
bitchCount++;
Rect_t srcRect = { 0, 0, w, h };
Rect_t dstRect = { x, y, w, h };
pRenderContext->CopyRenderTargetToTextureEx( m_ShadowAllocator.GetTexture(), 0, &srcRect, &dstRect );
}
}
 
}
Assert(0);
else
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
{
continue;
// 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" );
}
}


CViewSetup shadowView;
// Only clear the dirty flag if the caster isn't animating
shadowView.m_flAspectRatio = 1.0f;
if ( (shadow.m_Flags & SHADOW_FLAGS_ANIMATING_SOURCE) == 0 )
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 );
shadow.m_Flags &= ~SHADOW_FLAGS_TEXTURE_DIRTY;
}
}


// Set depth bias factors specific to this flashlight
SetRenderToTextureShadowTexCoords( shadow.m_ShadowHandle, x, y, w, h );
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 );
}
}
 
else if ( bPreviouslyUsingLODShadow )
SetViewFlashlightState( nActiveDepthShadowCount, pActiveDepthShadows );
{
// 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;
}
}


 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Re-renders all shadow textures for shadow casters that lie in the leaf list
// "Draws" the shadow LOD, which really means just set up the blobby shadow
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void SetupBonesOnBaseAnimating( C_BaseAnimating *&pBaseAnimating )
void CClientShadowMgr::DrawRenderToTextureShadowLOD( unsigned short clientShadowHandle )
{
{
pBaseAnimating->SetupBones( NULL, -1, -1, gpGlobals->curtime );
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;
}
}
}




void CClientShadowMgr::ComputeShadowTextures( const CViewSetup &view, int leafCount, LeafIndex_t* pLeafList )
//-----------------------------------------------------------------------------
// Advances to the next frame,
//-----------------------------------------------------------------------------
void CClientShadowMgr::AdvanceFrame()
{
{
VPROF_BUDGET( "CClientShadowMgr::ComputeShadowTextures", VPROF_BUDGETGROUP_SHADOW_RENDERING );
// We're starting the next frame
m_ShadowAllocator.AdvanceFrame();
}


if ( !m_RenderToTextureActive || (r_shadows.GetInt() == 0) || r_shadows_gamecontrol.GetInt() == 0 )
return;


m_bThreaded = ( r_threaded_client_shadow_manager.GetBool() && g_pThreadPool->NumIdleThreads() );
//-----------------------------------------------------------------------------
// 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];


MDLCACHE_CRITICAL_SECTION();
// If this is not a flashlight which should use a shadow depth texture, skip!
// First grab all shadow textures we may want to render
if ( ( shadow.m_Flags & SHADOW_FLAGS_USE_DEPTH_TEXTURE ) == 0 )
int nCount = s_VisibleShadowList.FindShadows( &view, leafCount, pLeafList );
continue;
if ( nCount == 0 )
return;


// FIXME: Add heuristics based on distance, etc. to futz with
const FlashlightState_t& flashlightState = shadowmgr->GetFlashlightState( shadow.m_ShadowHandle );
// the shadow allocator + to select blobby shadows


CMatRenderContextPtr pRenderContext( materials );
// Bail if this flashlight doesn't want shadows
if ( !flashlightState.m_bEnableShadows )
continue;


PIXEVENT( pRenderContext, "Render-To-Texture Shadows" );
// Calculate an AABB around the shadow frustum
Vector vecAbsMins, vecAbsMaxs;
CalculateAABBFromProjectionMatrix( shadow.m_WorldToShadow, &vecAbsMins, &vecAbsMaxs );


// Clear to white (color unused), black alpha
Frustum_t viewFrustum;
pRenderContext->ClearColor4ub( 255, 255, 255, 0 );
GeneratePerspectiveFrustum( viewSetup.origin, viewSetup.angles, viewSetup.zNear, viewSetup.zFar, viewSetup.fov, viewSetup.m_flAspectRatio, viewFrustum );


// No height clip mode please.
// FIXME: Could do other sorts of culling here, such as frustum-frustum test, distance etc.
MaterialHeightClipMode_t oldHeightClipMode = pRenderContext->GetHeightClipMode();
// If it's not in the view frustum, move on
pRenderContext->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE );
if ( R_CullBox( vecAbsMins, vecAbsMaxs, viewFrustum ) )
{
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
continue;
}


// No projection matrix (orthographic mode)
if ( nActiveDepthShadowCount >= nMaxDepthShadows )
// FIXME: Make it work for projective shadows?
{
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
static bool s_bOverflowWarning = false;
pRenderContext->PushMatrix();
if ( !s_bOverflowWarning )
pRenderContext->LoadIdentity();
{
pRenderContext->Scale( 1, -1, 1 );
Warning( "Too many depth textures rendered in a single view!\n" );
pRenderContext->Ortho( 0, 0, 1, 1, -9999, 0 );
Assert( 0 );
s_bOverflowWarning = true;
}
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
continue;
}
 
pActiveDepthShadows[nActiveDepthShadowCount++] = i;
}
return nActiveDepthShadowCount;
}


pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PushMatrix();


pRenderContext->PushRenderTargetAndViewport( m_ShadowAllocator.GetTexture() );
//-----------------------------------------------------------------------------
// 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;


if ( !IsX360() && m_bRenderTargetNeedsClear )
Assert( nActiveFlashlightCount<= 1 );
if ( nActiveFlashlightCount > 0 )
{
Assert( ( m_Shadows[ pActiveFlashlights[0] ].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) != 0 );
shadowmgr->SetFlashlightRenderState( pActiveFlashlights[0] );
}
else
{
{
// don't need to clear absent depth buffer
shadowmgr->SetFlashlightRenderState( SHADOW_HANDLE_INVALID );
pRenderContext->ClearBuffers( true, false );
m_bRenderTargetNeedsClear = false;
}
}
}


int nMaxShadows = r_shadowmaxrendered.GetInt();
int nModelsRendered = 0;
int i;


if ( m_bThreaded && g_pThreadPool->NumIdleThreads() )
//-----------------------------------------------------------------------------
// 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 )
{
{
s_NPCShadowBoneSetups.RemoveAll();
ClientShadow_t& shadow = m_Shadows[ pActiveDepthShadows[j] ];
s_NonNPCShadowBoneSetups.RemoveAll();


for (i = 0; i < nCount; ++i)
CTextureReference shadowDepthTexture;
bool bGotShadowDepthTexture = LockShadowDepthTexture( &shadowDepthTexture );
if ( !bGotShadowDepthTexture )
{
{
const VisibleShadowInfo_t &info = s_VisibleShadowList.GetVisibleShadow(i);
// If we don't get one, that means we have too many this frame so bind no depth texture
if ( nModelsRendered < nMaxShadows )
static int bitchCount = 0;
if( bitchCount < 10 )
{
{
if ( BuildSetupListForRenderToTextureShadow( info.m_hShadow, info.m_flArea ) )
Warning( "Too many shadow maps this frame!\n"  );
{
bitchCount++;
++nModelsRendered;
}
}
}
Assert(0);
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, NULL, 0 );
continue;
}
}


ParallelProcess( s_NPCShadowBoneSetups.Base(), s_NPCShadowBoneSetups.Count(), &SetupBonesOnBaseAnimating );
CViewSetup shadowView;
ParallelProcess( s_NonNPCShadowBoneSetups.Base(), s_NonNPCShadowBoneSetups.Count(), &SetupBonesOnBaseAnimating );
shadowView.m_flAspectRatio = 1.0f;
 
shadowView.x = shadowView.y = 0;
nModelsRendered = 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;


for (i = 0; i < nCount; ++i)
// Can turn on all light frustum overlays or per light with flashlightState parameter...
{
if ( bDebugFrustum || flashlightState.m_bDrawShadowFrustum )
const VisibleShadowInfo_t &info = s_VisibleShadowList.GetVisibleShadow(i);
if ( nModelsRendered < nMaxShadows )
{
{
if ( DrawRenderToTextureShadow( info.m_hShadow, info.m_flArea ) )
DebugDrawFrustum( shadowView.origin, shadow.m_WorldToShadow );
{
++nModelsRendered;
}
}
else
{
DrawRenderToTextureShadowLOD( info.m_hShadow );
}
}
}


// Render to the backbuffer again
// Set depth bias factors specific to this flashlight
pRenderContext->PopRenderTargetAndViewport();
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->SetShadowDepthBiasFactors( flashlightState.m_flShadowSlopeScaleDepthBias, flashlightState.m_flShadowDepthBias );


// Restore the matrices
// Render to the shadow depth texture with appropriate view
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
view->UpdateShadowDepthTexture( m_DummyColorTexture, shadowDepthTexture, shadowView );
pRenderContext->PopMatrix();


pRenderContext->MatrixMode( MATERIAL_VIEW );
// Associate the shadow depth texture and stencil bit with the flashlight for use during scene rendering
pRenderContext->PopMatrix();
shadowmgr->SetFlashlightDepthTexture( shadow.m_ShadowHandle, shadowDepthTexture, 0 );
}


pRenderContext->SetHeightClipMode( oldHeightClipMode );
SetViewFlashlightState( nActiveDepthShadowCount, pActiveDepthShadows );
 
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
//-----------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------
// Re-renders all shadow textures for shadow casters that lie in the leaf list
bool CClientShadowMgr::LockShadowDepthTexture( CTextureReference *shadowDepthTexture )
//-----------------------------------------------------------------------------
static void SetupBonesOnBaseAnimating( C_BaseAnimating *&pBaseAnimating )
{
{
for ( int i=0; i < m_DepthTextureCache.Count(); i++ ) // Search for cached shadow depth texture
pBaseAnimating->SetupBones( NULL, -1, -1, gpGlobals->curtime );
{
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 )
void CClientShadowMgr::ComputeShadowTextures( const CViewSetup &view, int leafCount, LeafIndex_t* pLeafList )
{
{
Assert( m_Shadows.IsValidIndex( shadowHandle ) );
VPROF_BUDGET( "CClientShadowMgr::ComputeShadowTextures", VPROF_BUDGETGROUP_SHADOW_RENDERING );


CClientShadowMgr::ClientShadow_t &shadow = m_Shadows[ shadowHandle ];
if ( !m_RenderToTextureActive || (r_shadows.GetInt() == 0) || r_shadows_gamecontrol.GetInt() == 0 )
if( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 )
return;
return;


// shadow.m_pTargetRenderable = pRenderable;
m_bThreaded = false;//( r_threaded_client_shadow_manager.GetBool() && g_pThreadPool->NumIdleThreads() );
shadow.m_hTargetEntity = targetEntity;
}


 
MDLCACHE_CRITICAL_SECTION();
void CClientShadowMgr::SetFlashlightLightWorld( ClientShadowHandle_t shadowHandle, bool bLightWorld )
// First grab all shadow textures we may want to render
{
int nCount = s_VisibleShadowList.FindShadows( &view, leafCount, pLeafList );
Assert( m_Shadows.IsValidIndex( shadowHandle ) );
if ( nCount == 0 )
 
ClientShadow_t &shadow = m_Shadows[ shadowHandle ];
if( ( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT ) == 0 )
return;
return;


if ( bLightWorld )
// FIXME: Add heuristics based on distance, etc. to futz with
{
// the shadow allocator + to select blobby shadows
shadow.m_Flags |= SHADOW_FLAGS_LIGHT_WORLD;
}
else
{
shadow.m_Flags &= ~SHADOW_FLAGS_LIGHT_WORLD;
}
}


CMatRenderContextPtr pRenderContext( materials );


bool CClientShadowMgr::IsFlashlightTarget( ClientShadowHandle_t shadowHandle, IClientRenderable *pRenderable )
PIXEVENT( pRenderContext, "Render-To-Texture Shadows" );
{
ClientShadow_t &shadow = m_Shadows[ shadowHandle ];


if( shadow.m_hTargetEntity->GetClientRenderable() == pRenderable )
// Clear to white (color unused), black alpha
return true;
pRenderContext->ClearColor4ub( 255, 255, 255, 0 );


C_BaseEntity *pChild = shadow.m_hTargetEntity->FirstMoveChild();
// No height clip mode please.
while( pChild )
MaterialHeightClipMode_t oldHeightClipMode = pRenderContext->GetHeightClipMode();
{
pRenderContext->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE );
if( pChild->GetClientRenderable()==pRenderable )
return true;


pChild = pChild->NextMovePeer();
// 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;
}
}
 
return false;
int nMaxShadows = r_shadowmaxrendered.GetInt();
}
int nModelsRendered = 0;
const Vector &CClientShadowMgr::GetShadowDirection( ClientShadowHandle_t shadowHandle ) const
int i;
{
 
Assert( shadowHandle != CLIENTSHADOW_INVALID_HANDLE );
if ( m_bThreaded && g_pThreadPool->NumIdleThreads() )
IClientRenderable* pRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[shadowHandle].m_Entity );
Assert( pRenderable );
if ( !IsShadowingFromWorldLights() )
{
{
return GetShadowDirection( pRenderable );
s_NPCShadowBoneSetups.RemoveAll();
}
s_NonNPCShadowBoneSetups.RemoveAll();
 
Vector &vecResult = AllocTempVector();
for (i = 0; i < nCount; ++i)
vecResult = m_Shadows[shadowHandle].m_ShadowDir;
{
const VisibleShadowInfo_t &info = s_VisibleShadowList.GetVisibleShadow(i);
// Allow the renderable to override the default
if ( nModelsRendered < nMaxShadows )
pRenderable->GetShadowCastDirection( &vecResult, GetActualShadowCastType( pRenderable ) );
{
if ( BuildSetupListForRenderToTextureShadow( info.m_hShadow, info.m_flArea ) )
return vecResult;
{
}
++nModelsRendered;
}
void CClientShadowMgr::UpdateShadowDirectionFromLocalLightSource( ClientShadowHandle_t shadowHandle )
}
{
}
Assert( shadowHandle != CLIENTSHADOW_INVALID_HANDLE );
 
ParallelProcess( "NPCShadowBoneSetups", s_NPCShadowBoneSetups.Base(), s_NPCShadowBoneSetups.Count(), &SetupBonesOnBaseAnimating );
ClientShadow_t& shadow = m_Shadows[shadowHandle];
ParallelProcess( "NonNPCShadowBoneSetups", s_NonNPCShadowBoneSetups.Base(), s_NonNPCShadowBoneSetups.Count(), &SetupBonesOnBaseAnimating );
 
IClientRenderable* pRenderable = ClientEntityList().GetClientRenderableFromHandle( shadow.m_Entity );
nModelsRendered = 0;
// 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;
for (i = 0; i < nCount; ++i)
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
const VisibleShadowInfo_t &info = s_VisibleShadowList.GetVisibleShadow(i);
float flMinBrightnessSqr = r_worldlight_mincastintensity.GetFloat();
if ( nModelsRendered < nMaxShadows )
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
if ( DrawRenderToTextureShadow( info.m_hShadow, info.m_flArea ) )
// TODO: Could switch to using blobby shadow in this case
{
lightPos.Init( FLT_MAX, FLT_MAX, FLT_MAX );
++nModelsRendered;
}
}
}
}
else
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();
DrawRenderToTextureShadowLOD( info.m_hShadow );
}
}
if ( targetLightPos.x == FLT_MAX )
}
{
 
targetLightPos = origin - 200.0f * GetShadowDirection();
// Render to the backbuffer again
}
pRenderContext->PopRenderTargetAndViewport();
 
// lerp light pos
// Restore the matrices
Vector v1 = origin - shadow.m_CurrentLightPos;
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
v1.NormalizeInPlace();
pRenderContext->PopMatrix();
 
Vector v2 = origin - shadow.m_TargetLightPos;
pRenderContext->MatrixMode( MATERIAL_VIEW );
v2.NormalizeInPlace();
pRenderContext->PopMatrix();
 
// SAULUNDONE: caused over top sweeping far too often
pRenderContext->SetHeightClipMode( oldHeightClipMode );
#if 0
 
if ( v1.Dot( v2 ) < 0.0f )
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
{
{
// if change in shadow angle is more than 90 degrees, lerp over the renderable's top to avoid long sweeping shadows
*shadowDepthTexture = m_DepthTextureCache[i];
Vector fakeOverheadLightPos( origin.x, origin.y, origin.z + 200.0f );
m_DepthTextureCacheLocks[i] = true;
if( shadow.m_LightPosLerp < 0.5f )
return true;
{
}
lightPos = Lerp( 2.0f * shadow.m_LightPosLerp, currLightPos, fakeOverheadLightPos );
}
}
 
else
return false; // Didn't find it...
{
}
lightPos = Lerp( 2.0f * shadow.m_LightPosLerp - 1.0f, fakeOverheadLightPos, targetLightPos );
 
}
//------------------------------------------------------------------
}
// Unlock shadow depth texture for use on subsequent views / frames
else
//------------------------------------------------------------------
#endif
void CClientShadowMgr::UnlockAllShadowDepthTextures()
{
{
lightPos = Lerp( shadow.m_LightPosLerp, currLightPos, targetLightPos );
for (int i=0; i< m_DepthTextureCache.Count(); i++ )
}
{
m_DepthTextureCacheLocks[i] = false;
if ( shadow.m_LightPosLerp >= 1.0f )
}
{
SetViewFlashlightState( 0, NULL );
shadow.m_CurrentLightPos = shadow.m_TargetLightPos;
}
 
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 )
else if ( shadow.m_LightPosLerp >= 1.0f )
{
{
// check if we have a new closest light position and start a new lerp
// Check if we have a new closest light position and start a new lerp
float flDistSq = ( lightPos - shadow.m_CurrentLightPos ).LengthSqr();
float flDistSq = ( lightPos - shadow.m_CurrentLightPos ).LengthSqr();
 
if ( flDistSq > 1.0f )
if ( flDistSq > 1.0f )
{
{
// light position has changed, which means we got a new light source. Initiate a lerp
// Light position has changed, which means we got a new light source. Initiate a lerp
shadow.m_TargetLightPos = lightPos;
shadow.m_TargetLightPos = lightPos;
shadow.m_LightPosLerp = 0.0f;
shadow.m_LightPosLerp = 0.0f;
}
}
 
lightPos = shadow.m_CurrentLightPos;
lightPos = shadow.m_CurrentLightPos;
}
}
 
if ( lightPos.x == FLT_MAX )
if ( lightPos.x == FLT_MAX )
{
{
lightPos = origin - 200.0f * GetShadowDirection();
lightPos = origin - 200.0f * GetShadowDirection();
}
}
 
Vector vecResult( origin - lightPos );
Vector vecResult( origin - lightPos );
vecResult.NormalizeInPlace();
vecResult.NormalizeInPlace();
 
vecResult.z *= r_worldlight_shortenfactor.GetFloat();
vecResult.z *= r_worldlight_shortenfactor.GetFloat();
vecResult.NormalizeInPlace();
vecResult.NormalizeInPlace();
 
shadow.m_ShadowDir = vecResult;
shadow.m_ShadowDir = vecResult;
 
if ( r_worldlight_debug.GetBool() )
if ( r_worldlight_debug.GetBool() )
{
{
NDebugOverlay::Line( lightPos, origin, 255, 255, 0, false, 0.0f );
NDebugOverlay::Line( lightPos, origin, 255, 255, 0, false, 0.0f );
}
}
}
}
 
void CClientShadowMgr::UpdateDirtyShadow( ClientShadowHandle_t handle )
//-----------------------------------------------------------------------------
{
// Purpose:
Assert( m_Shadows.IsValidIndex( handle ) );
//-----------------------------------------------------------------------------
   
void CClientShadowMgr::UpdateDirtyShadow( ClientShadowHandle_t handle )
if( IsShadowingFromWorldLights() )
{
UpdateShadowDirectionFromLocalLightSource( handle );
Assert( m_Shadows.IsValidIndex( handle ) );
   
   
UpdateProjectedTextureInternal( handle, false );
if ( IsShadowingFromWorldLights() )
}
UpdateShadowDirectionFromLocalLightSource( handle );
   
void WorldLightCastShadowCallback(IConVar *pVar, const char *pszOldValue, float flOldValue)
UpdateProjectedTextureInternal( handle, false );
{
}
s_ClientShadowMgr.SetShadowFromWorldLightsEnabled(r_worldlight_castshadows.GetBool());
 
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void WorldLightCastShadowCallback( IConVar *pVar, const char *pszOldValue, float flOldValue )
{
s_ClientShadowMgr.SetShadowFromWorldLightsEnabled( r_worldlight_castshadows.GetBool() );
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CClientShadowMgr::SetShadowFromWorldLightsEnabled( bool bEnabled )
void CClientShadowMgr::SetShadowFromWorldLightsEnabled( bool bEnabled )
{
{
if(bEnabled == IsShadowingFromWorldLights())
if ( bEnabled == IsShadowingFromWorldLights() )
return;
return;
   
   
Line 4,417: Line 4,716:
void CShadowProxy::OnBind( void *pProxyData )
void CShadowProxy::OnBind( void *pProxyData )
{
{
unsigned short clientShadowHandle = ( unsigned short )pProxyData;
unsigned short clientShadowHandle = ( unsigned short )(int)pProxyData&0xffff;
ITexture* pTex = s_ClientShadowMgr.GetShadowTexture( clientShadowHandle );
ITexture* pTex = s_ClientShadowMgr.GetShadowTexture( clientShadowHandle );
m_BaseTextureVar->SetTextureValue( pTex );
m_BaseTextureVar->SetTextureValue( pTex );
Line 4,501: Line 4,800:
void CShadowModelProxy::OnBind( void *pProxyData )
void CShadowModelProxy::OnBind( void *pProxyData )
{
{
unsigned short clientShadowHandle = ( unsigned short )pProxyData;
unsigned short clientShadowHandle = ( unsigned short )((int)pProxyData&0xffff);
ITexture* pTex = s_ClientShadowMgr.GetShadowTexture( clientShadowHandle );
ITexture* pTex = s_ClientShadowMgr.GetShadowTexture( clientShadowHandle );
m_BaseTextureVar->SetTextureValue( pTex );
m_BaseTextureVar->SetTextureValue( pTex );
Line 4,525: Line 4,824:


EXPOSE_INTERFACE( CShadowModelProxy, IMaterialProxy, "ShadowModel" IMATERIAL_PROXY_INTERFACE_VERSION );
EXPOSE_INTERFACE( CShadowModelProxy, IMaterialProxy, "ShadowModel" IMATERIAL_PROXY_INTERFACE_VERSION );
</source>
</source>

Revision as of 23:06, 30 November 2021

//========= 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;

//-----------------------------------------------------------------------------
// Purpose: Calculate intensity ratio for a worldlight by distance
//-----------------------------------------------------------------------------
static float Engine_WorldLightDistanceFalloff( const dworldlight_t *wl, const Vector &delta )
{
	float falloff;

	switch ( wl->type )
	{
	case emit_surface:
		// Cull out stuff that's too far
		if ( wl->radius != 0 )
		{
			if ( DotProduct( delta, delta ) > ( wl->radius * wl->radius ) )
				return 0.0f;
		}

		return InvRSquared( delta );

	case emit_skylight:
		return 1.0f;

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

		return falloff;

	case emit_skyambient:
		return 1.0f;

	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
//-----------------------------------------------------------------------------
CWorldLights::CWorldLights() : CAutoGameSystem( "World lights" )
{
	m_nWorldLights = 0;
	m_pWorldLights = nullptr;
}

//-----------------------------------------------------------------------------
// Purpose: Clear worldlights, free memory
//-----------------------------------------------------------------------------
void CWorldLights::Clear()
{
	m_nWorldLights = 0;

	if ( m_pWorldLights )
	{
		delete[] m_pWorldLights;
		m_pWorldLights = nullptr;
	}
}

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

	if ( ( g_pEngineServer = static_cast<IVEngineServer *>( factories.appSystemFactory( INTERFACEVERSION_VENGINESERVER, nullptr ) ) ) == nullptr )
		return false;

	return true;
}

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

	// Open map
	FileHandle_t hFile = g_pFullFileSystem->Open( pszMapName, "rb" );
	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
	// can safely assume that the engine did this for us
	dheader_t hdr;
	g_pFullFileSystem->Read( &hdr, sizeof( hdr ), hFile );

	// 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
	// This can happen when, for example, the map is compiled in HDR mode only
	// So move on to the HDR worldlights lump
	if ( lightLump.filelen == 0 )
	{
		lightLump = hdr.lumps[LUMP_WORLDLIGHTS_HDR];
	}

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

		// Close file
		g_pFullFileSystem->Close( hFile );
		return;
	}

	g_pFullFileSystem->Seek( hFile, lightLump.fileofs, FILESYSTEM_SEEK_HEAD );

	// Allocate memory for the worldlights
	m_nWorldLights = lightLump.filelen / sizeof( dworldlight_t );
	m_pWorldLights = new dworldlight_t[m_nWorldLights];

	// Read worldlights then close
	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];

		// Skip skyambient
		if ( light->type == emit_skyambient )
		{
			//engine->Con_NPrintf( i, "%d: skyambient", i );
			continue;
		}

		// Handle sun
		if ( light->type == emit_skylight )
		{
			// Calculate sun position
			Vector vecAbsStart = vecPosition + Vector( 0, 0, 30 );
			Vector vecAbsEnd = vecAbsStart - ( light->normal * MAX_TRACE_LENGTH );

			trace_t tr;
			UTIL_TraceLine( vecPosition, vecAbsEnd, MASK_OPAQUE, nullptr, COLLISION_GROUP_NONE, &tr );

			// If we didn't hit anything then we have a problem
			if ( !tr.DidHit() )
			{
				//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
			// this worldlight
			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
			// manager uses the default shadow direction instead (should be the
			// sun direction)

			delete[] pvs;

			return false;
		}

		// Calculate square distance to this worldlight
		Vector vecDelta = light->origin - vecPosition;
		float flDistSqr = vecDelta.LengthSqr();
		float flRadiusSqr = light->radius * light->radius;

		// Skip lights that are out of our radius
		if ( flRadiusSqr > 0 && flDistSqr >= flRadiusSqr )
		{
			//engine->Con_NPrintf( i, "%d: out-of-radius (dist: %d, radius: %d)", i, sqrt( flDistSqr ), light->radius );
			continue;
		}

		// Is it out of our PVS?
		if ( !g_pEngineServer->CheckOriginInPVS( light->origin, pvs, nPVSSize ) )
		{
			//engine->Con_NPrintf( i, "%d: out of PVS", i );
			continue;
		}

		// Calculate intensity at our position
		float flRatio = Engine_WorldLightDistanceFalloff( light, vecDelta );
		Vector vecIntensity = light->intensity * flRatio;

		// Is this light more intense than the one we already found?
		if ( vecIntensity.LengthSqr() <= vecLightBrightness.LengthSqr() )
		{
			//engine->Con_NPrintf( i, "%d: too dim", i );
			continue;
		}

		// Can we see the light?
		trace_t tr;
		Vector vecAbsStart = vecPosition + Vector( 0, 0, 30 );
		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
//-----------------------------------------------------------------------------
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 );