//========= Copyright © 1996-2005, 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 "ImageLoader.h"
#include "materialsystem/IMaterialProxy.h"
#include "materialsystem/IMaterialVar.h"
#include "materialsystem/IMaterial.h"
#include "materialsystem/IMesh.h"
#include "utlmultilist.h"
#include "CollisionUtils.h"
#include "IVRenderView.h"
#include "tier0/vprof.h"
#include "engine/ivmodelinfo.h"
#include "view_shared.h"
#include "engine/IVDebugOverlay.h"
#include "engine/IStaticPropMgr.h"

// 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", "1" );

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

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

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

private:
	typedef unsigned short FragmentHandle_t;

	enum
	{
		INVALID_FRAGMENT_HANDLE = (FragmentHandle_t)~0,
		TEXTURE_PAGE_SIZE	=	1024,
		MAX_TEXTURE_POWER	=	8,
		MAX_TEXTURE_SIZE	=	(1 << MAX_TEXTURE_POWER),
		MIN_TEXTURE_POWER	=   4,
		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();
	}

	// GR: 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 );
}

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?!?
	m_Blocks[0].m_FragmentPower = 4;	// 128 x 16
	m_Blocks[1].m_FragmentPower = 5;	// 64 x 32
	m_Blocks[2].m_FragmentPower = 6;	// 32 x 64
	m_Blocks[3].m_FragmentPower = 6;		 
	m_Blocks[4].m_FragmentPower = 7;	// 24 x 128
	m_Blocks[5].m_FragmentPower = 7;
	m_Blocks[6].m_FragmentPower = 7;
	m_Blocks[7].m_FragmentPower = 7;
	m_Blocks[8].m_FragmentPower = 7;
	m_Blocks[9].m_FragmentPower = 7;
	m_Blocks[10].m_FragmentPower = 8;	// 6 x 256
	m_Blocks[11].m_FragmentPower = 8;	 
	m_Blocks[12].m_FragmentPower = 8;
	m_Blocks[13].m_FragmentPower = 8;
	m_Blocks[14].m_FragmentPower = 8;
	m_Blocks[15].m_FragmentPower = 8;

	// 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 )
	{
		if (m_Cache[i].m_List != m_Fragments.InvalidIndex())
		{
			m_Fragments.DestroyList(m_Cache[i].m_List);
			m_Cache[i].m_List = m_Fragments.InvalidIndex();
		}
	}
}


//-----------------------------------------------------------------------------
// 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 = 0;
		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 )
{
	if (m_Textures[h].m_Fragment != INVALID_FRAGMENT_HANDLE)
	{
		MarkUnused(m_Textures[h].m_Fragment);
		DisconnectTextureFromFragment( m_Textures[h].m_Fragment );
	}
	m_Textures.Remove(h);
}


//-----------------------------------------------------------------------------
// Disconnect texture from fragment
//-----------------------------------------------------------------------------
void CTextureAllocator::DisconnectTextureFromFragment( FragmentHandle_t f )
{
	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;
	}
}


//-----------------------------------------------------------------------------
// Mark texture as being used...
//-----------------------------------------------------------------------------
bool CTextureAllocator::UseTexture( TextureHandle_t h, bool bWillRedraw, float flArea )
{
	TextureInfo_t& info = m_Textures[h];

	// 4 is the minimum power we have allocated
	int nDesiredPower = 4;
	int nDesiredWidth = 16;
	while ( (nDesiredWidth * nDesiredWidth) < flArea )
	{
		if ( nDesiredPower >= info.m_Power )
		{
			nDesiredPower = info.m_Power;
			break;
		}

		++nDesiredPower;
		nDesiredWidth *= 2;
	}

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

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

	FragmentHandle_t f;
	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;
		}
	}

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

	// 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()
{
	// Doing it this way prevents cache thrash when rendering
	// multiple views in a single frame.
	m_CurrentFrame = gpGlobals->framecount;
}


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


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

	// Inherited from IClientShadowMgr
	virtual bool Init();
	virtual void Shutdown();
	virtual void LevelInitPreEntity();
	virtual void LevelInitPostEntity() {}
	virtual void LevelShutdownPreEntity() {}
	virtual void LevelShutdownPostEntity();
	virtual void PreRender ( );
	virtual void Update ( float frametime ) {}

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


	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( C_BaseEntity *pEntity, 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 *pView, int leafCount, LeafIndex_t* pLeafList );

	// 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( C_BaseEntity *pEntity, const Vector &vecAbsCenter, float flRadius, Vector *pAbsMins, Vector *pAbsMaxs );

	// Returns true if the shadow is far enough to want to use blobby shadows
	bool ShouldUseBlobbyShadows( float flRadius, float flScreenArea );

	bool WillParentRenderBlobbyShadow( C_BaseEntity *pBaseEntity );

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

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),
	};

	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_LastOrigin;
		QAngle					m_LastAngles;
		TextureHandle_t			m_ShadowTexture;
		int						m_nRenderFrame;
	};

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

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

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

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

	// Builds matrices transforming from world space to shadow space
	void BuildGeneralWorldToShadowMatrix( VMatrix& worldToShadow,
		const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec );
	void BuildOrthoWorldToShadowMatrix( VMatrix& worldToShadow,
		const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec );
	void BuildPerspectiveWorldToFlashlightMatrix( VMatrix& worldToShadow,
		const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec, float fovDegrees,
		float zNear, float zFar );

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

	// Compute the shadow origin and attenuation start distance
	float ComputeLocalShadowOrigin( IClientEntity* pEnt, 
		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 *pEnt ) const;

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

	// Builds a more complex shadow...
	void BuildRenderToTextureShadow( IClientEntity* pEnt, 
			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( IClientEntity* pEnt, 
		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( IClientEntity *pClientEntity, 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( IClientEntity* pEnt, const Vector& mins, const Vector& maxs );

	// Clears the render target if necessary
	void ClearEntireRenderTarget();

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

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

private:
	Vector	m_SimpleShadowDir;
	color32	m_AmbientLightColor;
	CMaterialReference m_SimpleShadow;
	CMaterialReference m_RenderShadow;
	CMaterialReference m_RenderModelShadow;
	CUtlLinkedList< ClientShadow_t, ClientShadowHandle_t >	m_Shadows;
	CTextureAllocator m_ShadowAllocator;
	bool m_RenderToTextureActive;
	bool m_bRenderTargetNeedsClear;
	bool m_bUpdatingDirtyShadows;
	float m_flShadowCastDist;
	float m_flMinShadowArea;
	int m_nMaxModelsRendered;
	CUtlRBTree< ClientShadowHandle_t, unsigned short >	m_DirtyShadows;

	friend class CVisibleShadowList;
};


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


CClientShadowMgr::CClientShadowMgr() :
	m_DirtyShadows( 0, 0, ShadowHandleCompareFunc )
{
}

//-----------------------------------------------------------------------------
// Changes the shadow direction...
//-----------------------------------------------------------------------------
static void ShadowDir_f()
{
	Vector dir;
	if (engine->Cmd_Argc() == 1)
	{
		Vector dir = s_ClientShadowMgr.GetShadowDirection();
		Msg( "%.2f %.2f %.2f\n", dir.x, dir.y, dir.z );
		return;
	}

	if (engine->Cmd_Argc() == 4)
	{
		dir.x = atof( engine->Cmd_Argv(1) );
		dir.y = atof( engine->Cmd_Argv(2) );
		dir.z = atof( engine->Cmd_Argv(3) );
		s_ClientShadowMgr.SetShadowDirection(dir);
	}
}

static void ShadowAngles_f()
{
	Vector dir;
	QAngle angles;
	if (engine->Cmd_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 (engine->Cmd_Argc() == 4)
	{
		angles.x = atof( engine->Cmd_Argv(1) );
		angles.y = atof( engine->Cmd_Argv(2) );
		angles.z = atof( engine->Cmd_Argv(3) );
		AngleVectors( angles, &dir );
		s_ClientShadowMgr.SetShadowDirection(dir);
	}
}

static void ShadowColor_f()
{
	if (engine->Cmd_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 (engine->Cmd_Argc() == 4)
	{
		int r = atoi( engine->Cmd_Argv(1) );
		int g = atoi( engine->Cmd_Argv(2) );
		int b = atoi( engine->Cmd_Argv(3) );
		s_ClientShadowMgr.SetShadowColor(r, g, b);
	}
}

static void ShadowDistance_f()
{
	if (engine->Cmd_Argc() == 1)
	{
		float flDist = s_ClientShadowMgr.GetShadowDistance( );
		Msg( "Shadow distance %.2f\n", flDist );
		return;
	}

	if (engine->Cmd_Argc() == 2)
	{
		float flDistance = atof( engine->Cmd_Argv(1) );
		s_ClientShadowMgr.SetShadowDistance( flDistance );
	}
}

static void ShadowBlobbyCutoff_f()
{
	if (engine->Cmd_Argc() == 1)
	{
		float flArea = s_ClientShadowMgr.GetBlobbyCutoffArea( );
		Msg( "Cutoff area %.2f\n", flArea );
		return;
	}

	if (engine->Cmd_Argc() == 2)
	{
		float flArea = atof( engine->Cmd_Argv(1) );
		s_ClientShadowMgr.SetShadowBlobbyCutoffArea( flArea );
	}
}

static ConCommand r_shadowdir("r_shadowdir", ShadowDir_f, "Set shadow direction", FCVAR_CHEAT );
static ConCommand r_shadowangles("r_shadowangles", ShadowAngles_f, "Set shadow angles", FCVAR_CHEAT );
static ConCommand r_shadowcolor("r_shadowcolor", ShadowColor_f, "Set shadow color", FCVAR_CHEAT );
static ConCommand r_shadowdist("r_shadowdist", ShadowDistance_f, "Set shadow distance", FCVAR_CHEAT );
static ConCommand r_shadowblobbycutoff("r_shadowblobbycutoff", ShadowBlobbyCutoff_f, "some shadow stuff", FCVAR_CHEAT );

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 );
	m_nMaxModelsRendered = 8;

	if ( r_shadowrendertotexture.GetBool() )
	{
		InitRenderToTextureShadows();
	}

	materials->AddRestoreFunc( ShadowRestoreFunc );
	return true;
}

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

	materials->RemoveRestoreFunc( ShadowRestoreFunc );
}


//-----------------------------------------------------------------------------
// 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*)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();
	}
}


//-----------------------------------------------------------------------------
// 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];
	
	IClientEntity *pEnt = ClientEntityList().GetClientEntityFromHandle( shadow.m_Entity );
	if ( !pEnt )
		return;

	Vector mins, maxs;
	pEnt->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()
{
	m_bUpdatingDirtyShadows = true;

	for (ClientShadowHandle_t i = m_Shadows.Head(); i != m_Shadows.InvalidIndex(); i = m_Shadows.Next(i) )
	{
		UpdateProjectedTextureInternal( i, true );
	}
	m_DirtyShadows.RemoveAll();

	m_bUpdatingDirtyShadows = false;
}


//-----------------------------------------------------------------------------
// 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)
	{
		materials->Bind( m_RenderShadow );
		IMesh* pMesh = materials->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 ) )
	{
		IClientEntity *pEnt = ClientEntityList().GetClientEntityFromHandle( entity );
		int modelType = modelinfo->GetModelType( pEnt->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_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*)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->CreateShadow( 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;
	ClientShadowHandle_t shadowHandle = CreateProjectedTexture( invalidHandle, SHADOW_FLAGS_FLASHLIGHT );
	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 );

	IClientEntity *pEnt = ClientEntityList().GetClientEntityFromHandle( entity );
	if ( pEnt )
	{
		C_BaseEntity *pBaseEntity = pEnt->GetBaseEntity();
		Assert( !pBaseEntity->IsEFlagSet( EFL_DIRTY_SHADOWUPDATE ) );
		pBaseEntity->AddEFlags( EFL_DIRTY_SHADOWUPDATE );
	}

	// 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 )
{
	Vector lightXVec;
	Vector lightYVec( 0.0f, 0.0f, 1.0f );
	if( fabs( DotProduct( lightYVec, flashlightState.m_vecLightDirection ) ) > 0.9f )
	{
		// Don't want lightYVec and m_vecLightDirection to be parallel
		lightYVec.Init( 0.0f, 1.0f, 0.0f );
	}
	CrossProduct( lightYVec, flashlightState.m_vecLightDirection, lightXVec );
	VectorNormalize( lightXVec );
	CrossProduct( flashlightState.m_vecLightDirection, lightXVec, lightYVec );
	VectorNormalize( lightYVec );

	BuildPerspectiveWorldToFlashlightMatrix( m_Shadows[shadowHandle].m_WorldToShadow, flashlightState.m_vecLightOrigin, flashlightState.m_vecLightDirection, 
		lightXVec, lightYVec, flashlightState.m_fVerticalFOVDegrees, flashlightState.m_NearZ, flashlightState.m_FarZ );

	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.
		IClientEntity *pEnt = ClientEntityList().GetClientEntityFromHandle( m_Shadows[handle].m_Entity );
		if ( pEnt )
		{
			C_BaseEntity *pBaseEntity = pEnt->GetBaseEntity();
			Assert( pBaseEntity );
			pBaseEntity->RemoveEFlags( EFL_DIRTY_SHADOWUPDATE );
		}
		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& worldToShadow,
	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...
	worldToShadow.SetBasisVectors( xvec, yvec, dir );
	worldToShadow.SetTranslation( origin );
	worldToShadow[3][0] = worldToShadow[3][1] = worldToShadow[3][2] = 0.0f;
	worldToShadow[3][3] = 1.0f;

	// Now do a general inverse to get worldToShadow
	MatrixInverseGeneral( worldToShadow, worldToShadow );
}

void CClientShadowMgr::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;
}

void CClientShadowMgr::BuildPerspectiveWorldToFlashlightMatrix( VMatrix& worldToShadow,
	const Vector& origin, const Vector& dir, const Vector& xvec, const Vector& yvec, float fovDegrees,
	float zNear, float zFar )
{
	// build the ortho shadow matrix to get us from world to shadow space.  We'll build the perspective
	// part separately and concatenate.
	VMatrix worldToShadowView;
	BuildOrthoWorldToShadowMatrix( worldToShadowView, origin, dir, xvec, yvec );

	VMatrix perspective;
	MatrixBuildPerspective( perspective, fovDegrees, fovDegrees, zNear, zFar );

	MatrixMultiply( perspective, worldToShadowView, worldToShadow );
}

//-----------------------------------------------------------------------------
// Compute the shadow origin and attenuation start distance
//-----------------------------------------------------------------------------
float CClientShadowMgr::ComputeLocalShadowOrigin( IClientEntity* pEnt, 
	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& worldToShadow, 
							const Vector2D& size, VMatrix& worldToTexture )
{
	// 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, worldToShadow, worldToTexture );
}


//-----------------------------------------------------------------------------
// 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( IClientEntity* pEnt, 
	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 = pEnt->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 );
	}
}


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
	{
		return SHADOWS_SIMPLE;
	}
}

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


//-----------------------------------------------------------------------------
// Builds a simple blobby shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::BuildOrthoShadow( IClientEntity* pEnt, 
		ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs)
{
	// Get the object's basis
	Vector vec[3];
	AngleVectors( pEnt->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
	vec[1] *= -1.0f;

	Vector vecShadowDir = GetShadowDirection( pEnt );

	// 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( pEnt, mins, maxs, localShadowDir, 2.0f, org );

	// Transform the local origin into world coordinates
	Vector worldOrigin = pEnt->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 worldToShadow, worldToTexture;
	BuildGeneralWorldToShadowMatrix( m_Shadows[handle].m_WorldToShadow, worldOrigin, vecShadowDir, xvec, yvec );
	BuildWorldToTextureMatrix( m_Shadows[handle].m_WorldToShadow, size, worldToTexture );
	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( pEnt );
	float maxHeight = flShadowCastDistance + falloffStart; //3.0f * sqrt( shadowArea );

	shadowmgr->ProjectShadow( m_Shadows[handle].m_ShadowHandle, worldOrigin, 
		vecShadowDir, worldToTexture, size, maxHeight, falloffStart, MAX_FALLOFF_AMOUNT, pEnt->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, 
		worldOrigin, vecShadowDir, size, maxHeight );
}


//-----------------------------------------------------------------------------
// Visualization....
//-----------------------------------------------------------------------------
void CClientShadowMgr::DrawRenderToTextureDebugInfo( IClientEntity* pEnt, const Vector& mins, const Vector& maxs )
{
	// Get the object's basis
	Vector vec[3];
	AngleVectors( pEnt->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
	vec[1] *= -1.0f;

	Vector vecSize;
	VectorSubtract( maxs, mins, vecSize );

	Vector vecOrigin = pEnt->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 ); 

	debugoverlay->AddTextOverlay( vecOrigin, 0, "%d", pEnt->entindex() );
}


//-----------------------------------------------------------------------------
// Builds a more complex shadow...
//-----------------------------------------------------------------------------
void CClientShadowMgr::BuildRenderToTextureShadow( IClientEntity* pEnt, 
		ClientShadowHandle_t handle, const Vector& mins, const Vector& maxs)
{
//	DrawRenderToTextureDebugInfo( pEnt, mins, maxs );

	// Get the object's basis
	Vector vec[3];
	AngleVectors( pEnt->GetRenderAngles(), &vec[0], &vec[1], &vec[2] );
	vec[1] *= -1.0f;

	Vector vecShadowDir = GetShadowDirection( pEnt );

	// 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 yvec = vec[vecIdx[0]];

	// Project it into a plane perpendicular to the shadow direction
	yvec -= vecShadowDir * DotProduct( vecShadowDir, yvec );
	VectorNormalize( yvec );

	// Compute the x vector
	Vector xvec;
	CrossProduct( yvec, vecShadowDir, xvec );

	// 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;
	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( pEnt, mins, maxs, localShadowDir, 1.0f, org );

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

	VMatrix worldToTexture;
	BuildOrthoWorldToShadowMatrix( m_Shadows[handle].m_WorldToShadow, worldOrigin, vecShadowDir, xvec, yvec );
	BuildWorldToTextureMatrix( m_Shadows[handle].m_WorldToShadow, size, worldToTexture );
	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( pEnt );
	float maxHeight = flShadowCastDistance + falloffStart; //3.0f * sqrt( shadowArea );

	shadowmgr->ProjectShadow( m_Shadows[handle].m_ShadowHandle, worldOrigin, 
		vecShadowDir, worldToTexture, size, maxHeight, falloffStart, MAX_FALLOFF_AMOUNT, pEnt->GetRenderOrigin() );

	// Compute extra clip planes to prevent poke-thru
	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, 
		worldOrigin, vecShadowDir, size, maxHeight );
}

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
		, 0.0 );
}

static void DebugDrawFrustum( const VMatrix &worldToFlashlight )
{
	VMatrix flashlightToWorld;
	MatrixInverseGeneral( worldToFlashlight, flashlightToWorld );
	
	LineDrawHelper( Vector( 0.0f, 0.0f, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), flashlightToWorld, 255, 0, 0 );
	LineDrawHelper( Vector( 0.0f, 0.0f, 1.0f ), Vector( 0.0f, 1.0f, 1.0f ), flashlightToWorld, 255, 0, 0 );
	LineDrawHelper( Vector( 0.0f, 1.0f, 1.0f ), Vector( 0.0f, 1.0f, 0.0f ), flashlightToWorld, 255, 0, 0 );
	LineDrawHelper( Vector( 0.0f, 1.0f, 0.0f ), Vector( 0.0f, 0.0f, 0.0f ), flashlightToWorld, 255, 0, 0 );
	LineDrawHelper( Vector( 1.0f, 0.0f, 0.0f ), Vector( 1.0f, 0.0f, 1.0f ), flashlightToWorld, 255, 0, 0 );
	LineDrawHelper( Vector( 1.0f, 0.0f, 1.0f ), Vector( 1.0f, 1.0f, 1.0f ), flashlightToWorld, 255, 0, 0 );
	LineDrawHelper( Vector( 1.0f, 1.0f, 1.0f ), Vector( 1.0f, 1.0f, 0.0f ), flashlightToWorld, 255, 0, 0 );
	LineDrawHelper( Vector( 1.0f, 1.0f, 0.0f ), Vector( 1.0f, 0.0f, 0.0f ), flashlightToWorld, 255, 0, 0 );
	LineDrawHelper( Vector( 0.0f, 0.0f, 0.0f ), Vector( 1.0f, 0.0f, 0.0f ), flashlightToWorld, 255, 0, 0 );
	LineDrawHelper( Vector( 0.0f, 0.0f, 1.0f ), Vector( 1.0f, 0.0f, 1.0f ), flashlightToWorld, 255, 0, 0 );
	LineDrawHelper( Vector( 0.0f, 1.0f, 1.0f ), Vector( 1.0f, 1.0f, 1.0f ), flashlightToWorld, 255, 0, 0 );
	LineDrawHelper( Vector( 0.0f, 1.0f, 0.0f ), Vector( 1.0f, 1.0f, 0.0f ), flashlightToWorld, 255, 0, 0 );
}

void CClientShadowMgr::BuildFlashlight( ClientShadowHandle_t handle )
{
	if( r_flashlightdrawfrustum.GetBool() )
	{
		DebugDrawFrustum( m_Shadows[handle].m_WorldToShadow );
	}

	shadowmgr->ProjectFlashlight( m_Shadows[handle].m_ShadowHandle, m_Shadows[handle].m_WorldToShadow );

	// Add the shadow to the client leaf system so it correctly marks 
	// leafs as being affected by a particular shadow
	if( r_flashlightmodels.GetBool() )
	{
		ClientLeafSystem()->ProjectFlashlight( m_Shadows[handle].m_ClientLeafShadowHandle, m_Shadows[handle].m_WorldToShadow );
	}
}

//-----------------------------------------------------------------------------
// Adds the child bounds to the bounding box
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddChildBounds( matrix3x4_t &worldToBBox, C_BaseEntity* pParent, Vector &vecMins, Vector &vecMaxs )
{
	Vector vecChildMins, vecChildMaxs;
	Vector vecNewChildMins, vecNewChildMaxs;
	matrix3x4_t childToBBox;

	C_BaseEntity *pChild = pParent->FirstMoveChild();
	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( worldToBBox, pChild->EntityToWorldTransform(), childToBBox );
			TransformAABB( childToBBox, vecChildMins, vecChildMaxs, vecNewChildMins, vecNewChildMaxs );
			VectorMin( vecMins, vecNewChildMins, vecMins );
			VectorMax( vecMaxs, vecNewChildMaxs, vecMaxs );
		}

		AddChildBounds( worldToBBox, pChild, vecMins, vecMaxs );
		pChild = pChild->NextMovePeer();
	}
}


//-----------------------------------------------------------------------------
// Compute a bounds for the entity + children
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeHierarchicalBounds( IClientRenderable *pRenderable, Vector &vecMins, Vector &vecMaxs )
{
	ShadowType_t shadowType = GetActualShadowCastType( pRenderable );

	C_BaseEntity *pBaseEntity = pRenderable->GetIClientUnknown()->GetBaseEntity();
	if (pBaseEntity)
	{
		pBaseEntity->GetShadowRenderBounds( vecMins, vecMaxs, shadowType );
		C_BaseEntity *pChild = pBaseEntity->FirstMoveChild();

		// Don't recurse down the tree when we hit a blobby shadow
		if ( pChild && shadowType != SHADOWS_SIMPLE )
		{
			matrix3x4_t worldToBBox;
			MatrixInvert( pBaseEntity->EntityToWorldTransform(), worldToBBox );
			AddChildBounds( worldToBBox, pBaseEntity, vecMins, vecMaxs );
		}
	}
	else
	{
		pRenderable->GetShadowRenderBounds( vecMins, vecMaxs, shadowType );
	}
}


//-----------------------------------------------------------------------------
// Shadow update functions
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateStudioShadow( IClientEntity *pEnt, ClientShadowHandle_t handle )
{
	if( !( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT ) )
	{
		Vector mins, maxs;
		ComputeHierarchicalBounds( pEnt, mins, maxs );

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

void CClientShadowMgr::UpdateBrushShadow( IClientEntity *pEnt, 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( pEnt, mins, maxs );

		ShadowType_t shadowType = GetActualShadowCastType( handle );
		if ( shadowType != SHADOWS_RENDER_TO_TEXTURE )
		{
			BuildOrthoShadow( pEnt, handle, mins, maxs );
		}
		else
		{
			BuildRenderToTextureShadow( pEnt, handle, mins, maxs );
		}
	}
	else
	{
		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( C_BaseEntity *pBaseEntity )
{
	if (!pBaseEntity)
		return false;

	C_BaseEntity *pMoveParent = pBaseEntity->GetMoveParent();
	if (!pMoveParent)
		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( pMoveParent );
	if ( shadowType == SHADOWS_NONE )
	{
		return WillParentRenderBlobbyShadow( pMoveParent );
	}

	return shadowType == SHADOWS_SIMPLE;
}


//-----------------------------------------------------------------------------
// Are we the child of a shadow with render-to-texture?
//-----------------------------------------------------------------------------
bool CClientShadowMgr::ShouldUseParentShadow( C_BaseEntity *pBaseEntity )
{
	if (!pBaseEntity)
		return false;

	C_BaseEntity *pMoveParent = pBaseEntity->GetMoveParent();
	if (!pMoveParent)
		return false;

	// Can't render into the parent if the parent is blobby
	ShadowType_t shadowType = GetActualShadowCastType( pMoveParent );
	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( pMoveParent );
	}

	// 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( "CClientShadowMgr::PreRender" );

	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() )
	{
		ClientShadowHandle_t& handle = m_DirtyShadows[ i ];
		Assert( m_Shadows.IsValidIndex( handle ) );
		UpdateProjectedTextureInternal( handle, false );
		i = m_DirtyShadows.NextInorder(i);
	}

	m_DirtyShadows.RemoveAll();

	m_bUpdatingDirtyShadows = false;
}


//-----------------------------------------------------------------------------
// Gets the entity whose shadow this shadow will render into
//-----------------------------------------------------------------------------
C_BaseEntity *CClientShadowMgr::GetParentShadowEntity( ClientShadowHandle_t handle )
{
	ClientShadow_t& shadow = m_Shadows[handle];
	IClientEntity *pEnt = ClientEntityList().GetClientEntityFromHandle( shadow.m_Entity );
	if ( pEnt )
	{
		C_BaseEntity *pBaseEntity = pEnt->GetBaseEntity();
		if ( ShouldUseParentShadow( pBaseEntity ) )
		{
			C_BaseEntity *pMoveParent = pBaseEntity->GetMoveParent();
			while ( GetActualShadowCastType( pMoveParent ) == SHADOWS_NONE )
			{
				pMoveParent = pMoveParent->GetMoveParent();
				Assert( pMoveParent );
			}
			return pMoveParent;
		}
	}
	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...
	C_BaseEntity *pMoveParent = GetParentShadowEntity( handle );
	if ( pMoveParent )
	{
		AddToDirtyShadowList( pMoveParent, bForce );
	}
}


//-----------------------------------------------------------------------------
// Marks a shadow as needing re-projection
//-----------------------------------------------------------------------------
void CClientShadowMgr::AddToDirtyShadowList( C_BaseEntity *pEnt, 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 ( pEnt->IsEFlagSet( EFL_DIRTY_SHADOWUPDATE ) )
		return;

	ClientShadowHandle_t handle = pEnt->GetShadowHandle();
	if ( handle == CLIENTSHADOW_INVALID_HANDLE )
		return;

#ifdef _DEBUG
	// Make sure everything's consistent
	if ( pEnt->GetShadowHandle() != CLIENTSHADOW_INVALID_HANDLE )
	{
		IClientEntity *pShadowEnt = ClientEntityList().GetClientEntityFromHandle( m_Shadows[handle].m_Entity );
		Assert( pEnt == pShadowEnt->GetBaseEntity() );
	}
#endif

	pEnt->AddEFlags( EFL_DIRTY_SHADOWUPDATE );
	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...
		C_BaseEntity *pMoveParent = GetParentShadowEntity( handle );
		if ( pMoveParent )
		{
			ClientShadowHandle_t parentHandle = pMoveParent->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....
	IClientEntity *pEnt = ClientEntityList().GetClientEntityFromHandle( shadow.m_Entity );
	if (!pEnt)
	{
		// Retire the shadow if the entity is gone
		DestroyShadow( handle );
		return;
	}

	// Clean up the shadow update bit.
	C_BaseEntity *pBaseEntity = pEnt->GetBaseEntity();
	Assert( pBaseEntity );

	if ( !pEnt->GetModel() )
	{
		pBaseEntity->RemoveEFlags( EFL_DIRTY_SHADOWUPDATE );
		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( pBaseEntity ) || WillParentRenderBlobbyShadow( pBaseEntity ) )
	{
		shadowmgr->EnableShadow( shadow.m_ShadowHandle, false );
		pBaseEntity->RemoveEFlags( EFL_DIRTY_SHADOWUPDATE );
		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 = pEnt->GetRenderOrigin();
	const QAngle& angles = pEnt->GetRenderAngles();

	if (force || (origin != shadow.m_LastOrigin) || (angles != shadow.m_LastAngles))
	{
		// Store off the new pos/orientation
		VectorCopy( origin, shadow.m_LastOrigin );
		VectorCopy( angles, shadow.m_LastAngles );

		const model_t *pModel = pEnt->GetModel();
		MaterialFogMode_t fogMode = materials->GetFogMode();
		materials->FogMode( MATERIAL_FOG_NONE );
		switch( modelinfo->GetModelType( pModel ) )
		{
		case mod_brush:
			UpdateBrushShadow( pEnt, handle );
			break;

		case mod_studio:
			UpdateStudioShadow( pEnt, handle );
			break;

		default:
			// Shouldn't get here if not a brush or studio
			Assert(0);
			break;
		}
		materials->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.
	pBaseEntity->RemoveEFlags( EFL_DIRTY_SHADOWUPDATE );
}


//-----------------------------------------------------------------------------
// Update a shadow
//-----------------------------------------------------------------------------
void CClientShadowMgr::UpdateProjectedTextureInternal( ClientShadowHandle_t handle, bool force )
{
	ClientShadow_t& shadow = m_Shadows[handle];

	if( shadow.m_Flags & SHADOW_FLAGS_FLASHLIGHT )
	{
		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 )
{
	if (handle == CLIENTSHADOW_INVALID_HANDLE)
		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( C_BaseEntity *pEntity, 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( pEntity );
	for (int i = 0; i < 3; ++i)
	{
		float flShadowCastDistance = GetShadowDistance( pEntity );
		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 )
	{
		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_FalloffOffset + 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( pSourceRenderable );
		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 )
{
	// Don't add a shadow cast by an object to itself...
	IClientRenderable* pSourceRenderable = ClientEntityList().GetClientRenderableFromHandle( m_Shadows[handle].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:
		shadowmgr->AddShadowToBrushModel( m_Shadows[handle].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 = ClientEntityList().GetBaseEntityFromHandle( m_Shadows[handle].m_Entity );
			if (pEnt && (pEnt->GetFlags() & (FL_NPC | FL_CLIENT)) )
			{
				staticpropmgr->AddShadowToStaticProp( m_Shadows[handle].m_ShadowHandle, pRenderable );
			}
		}
		else if( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT )
		{
			staticpropmgr->AddShadowToStaticProp( m_Shadows[handle].m_ShadowHandle, pRenderable );
		}
		break;

	case SHADOW_RECEIVER_STUDIO_MODEL:
		if( m_Shadows[handle].m_Flags & SHADOW_FLAGS_FLASHLIGHT )
		{
			C_BaseEntity *pEnt = pRenderable->GetIClientUnknown()->GetBaseEntity();
			Assert( pEnt );
			pEnt->CreateModelInstance();
			shadowmgr->AddShadowToModel( m_Shadows[handle].m_ShadowHandle, pEnt->GetModelInstance() );
		}
		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:
		C_BaseEntity *pEnt = pRenderable->GetIClientUnknown()->GetBaseEntity();
		if( pEnt && pEnt->GetModelInstance() != MODEL_INSTANCE_INVALID )
		{
			shadowmgr->RemoveAllShadowsFromModel( pEnt->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 );
}


//-----------------------------------------------------------------------------
// Draws all children shadows into our own
//-----------------------------------------------------------------------------
bool CClientShadowMgr::DrawShadowHierarchy( IClientEntity *pClientEntity, const ClientShadow_t &shadow, bool bChild )
{
	bool bDrewTexture = false;
	C_BaseEntity *pEnt = ((IClientRenderable*)pClientEntity)->GetIClientUnknown()->GetBaseEntity();

	// Stop traversing when we hit a blobby shadow
	ShadowType_t shadowType = GetActualShadowCastType( pEnt );
	if (pEnt && shadowType == SHADOWS_SIMPLE)
		return false;

	if (!pEnt || 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( pClientEntity->GetModel() );
			bDrawModelShadow = nModelType == mod_studio;
			bDrawBrushShadow = nModelType == mod_brush;
		}
    
		if ( bDrawModelShadow )
		{
			modelrender->DrawModelShadow( pClientEntity, pClientEntity->GetBody() );
			bDrewTexture = true;
		}
		else if ( bDrawBrushShadow )
		{
			render->DrawBrushModelShadow( pClientEntity );
		}
	}

	if (!pEnt)
		return bDrewTexture;

	C_BaseEntity *pChild;
	for ( pChild = pEnt->FirstMoveChild(); pChild; pChild = pChild->NextMovePeer() )
	{
		if (DrawShadowHierarchy( pChild, shadow, true ))
		{
			bDrewTexture = true;
		}
	}
	return bDrewTexture;
}


//-----------------------------------------------------------------------------
// 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
	if ( shadow.m_Flags & SHADOW_FLAGS_USING_LOD_SHADOW )
	{
		shadowmgr->SetShadowMaterial( shadow.m_ShadowHandle, m_RenderShadow, m_RenderModelShadow, (void*)clientShadowHandle );
	}

	// Mark texture as being used...
	bool bDirtyTexture = (shadow.m_Flags & SHADOW_FLAGS_TEXTURE_DIRTY) != 0;
	bool bDrewTexture = false;
	bool needsRedraw = m_ShadowAllocator.UseTexture( shadow.m_ShadowTexture, bDirtyTexture, flArea );
	if (needsRedraw || bDirtyTexture)
	{
		// shadow to be redrawn; for now, we'll always do it.
		IClientEntity *pEnt = ClientEntityList().GetClientEntityFromHandle( shadow.m_Entity );

		// Sets the viewport state
		int x, y, w, h;
		m_ShadowAllocator.GetTextureRect( shadow.m_ShadowTexture, x, y, w, h );
		materials->Viewport( x, y, w, h ); 

		// Clear the selected viewport only
		// GR: don't need to clear depth
		materials->ClearBuffers( true, false );

		materials->MatrixMode( MATERIAL_VIEW );
		materials->LoadMatrix( shadowmgr->GetInfo( shadow.m_ShadowHandle ).m_WorldToShadow );
   
		if (DrawShadowHierarchy(pEnt, shadow))
		{
			bDrewTexture = true;
		}

		// 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 ( shadow.m_Flags & SHADOW_FLAGS_USING_LOD_SHADOW )
	{
		// 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 );
	}

	shadow.m_Flags &= ~SHADOW_FLAGS_USING_LOD_SHADOW;
	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( shadow.m_ShadowHandle );
		shadow.m_Flags |= SHADOW_FLAGS_USING_LOD_SHADOW;
	}
}


#define SMALL_OBJECT_FIXUP_FACTOR 10

//-----------------------------------------------------------------------------
// Returns true if the shadow is far enough to want to use blobby shadows
//-----------------------------------------------------------------------------
bool CClientShadowMgr::ShouldUseBlobbyShadows( float flRadius, float flScreenArea )
{
	// Adjust the shadow area up for small objects; we don't want blobby shadows for 
	// really small things if we're real close to them...
	float flMaxFixupRadius = 20;
	float flMinFixupRadius = 6;
	if (flRadius < flMaxFixupRadius)
	{
		if (flRadius >= flMinFixupRadius)
			flScreenArea *= SMALL_OBJECT_FIXUP_FACTOR * (1.0f - (flRadius - flMinFixupRadius) / (flMaxFixupRadius - flMinFixupRadius) ) + 1.0f;
		else
			flScreenArea *= SMALL_OBJECT_FIXUP_FACTOR + 1.0f;
	}

	return (flScreenArea <= m_flMinShadowArea);
}


//-----------------------------------------------------------------------------
// 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;
	 float m_flRadius;
};

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:
	bool EnumShadow( unsigned short clientShadowHandle );
	float ComputeScreenArea( const Vector &vecCenter, float r ) const;
	void PrioritySort();

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


//-----------------------------------------------------------------------------
// Singleton instance
//-----------------------------------------------------------------------------
static CVisibleShadowList s_VisibleShadowList;



CVisibleShadowList::CVisibleShadowList() : m_ShadowsInView( 0, 64 ), m_PriorityIndex( 0, 64 ) 
{
}


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

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


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


//-----------------------------------------------------------------------------
// Visits every shadow in the list of leaves
//-----------------------------------------------------------------------------
bool CVisibleShadowList::EnumShadow( unsigned short clientShadowHandle )
{
	CClientShadowMgr::ClientShadow_t& shadow = s_ClientShadowMgr.m_Shadows[clientShadowHandle];
	
	// We don't need to bother with it
	if ( s_ClientShadowMgr.GetActualShadowCastType( clientShadowHandle ) != SHADOWS_RENDER_TO_TEXTURE )
		return true;

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

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

	// Don't bother with children of hierarchy; they will be drawn with their parents
	C_BaseEntity *pEntity = pRenderable->GetIClientUnknown()->GetBaseEntity();
	if ( s_ClientShadowMgr.ShouldUseParentShadow( pEntity ) || s_ClientShadowMgr.WillParentRenderBlobbyShadow( pEntity ) )
		return true;
	
	// 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( pEntity, 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 true;

	int i = m_ShadowsInView.AddToTail( );
	VisibleShadowInfo_t &info = m_ShadowsInView[i];
	info.m_hShadow = clientShadowHandle;
	info.m_vecAbsCenter = vecAbsCenter;
	info.m_flRadius = flRadius;
	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;

	return true;
}


//-----------------------------------------------------------------------------
// 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;
			}
		}
		swap( m_PriorityIndex[i], m_PriorityIndex[nLargestInd] );
	}
}


//-----------------------------------------------------------------------------
// Main entry point for finding shadows in the leaf list
//-----------------------------------------------------------------------------
int CVisibleShadowList::FindShadows( const CViewSetup *pView, int nLeafCount, LeafIndex_t *pLeafList )
{
	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;
}	


//-----------------------------------------------------------------------------
// Clears the render target if necessary
//-----------------------------------------------------------------------------
void CClientShadowMgr::ClearEntireRenderTarget()
{
	if ( m_bRenderTargetNeedsClear )
	{
		// Sets the viewport state
		int w, h;
		m_ShadowAllocator.GetTotalTextureSize( w, h );
		materials->Viewport( 0, 0, w, h ); 

		// Clear the selected viewport only
		// GR: don't need to clear depth
		materials->ClearBuffers( true, false );

		m_bRenderTargetNeedsClear = false;
	}
}


//-----------------------------------------------------------------------------
// Re-renders all shadow textures for shadow casters that lie in the leaf list
//-----------------------------------------------------------------------------
void CClientShadowMgr::ComputeShadowTextures( const CViewSetup *pView, int leafCount, LeafIndex_t* pLeafList )
{
	VPROF_BUDGET( "CClientShadowMgr::ComputeShadowTextures", VPROF_BUDGETGROUP_SHADOW_RENDERING );
	
	if (!m_RenderToTextureActive || (r_shadows.GetInt() == 0))
		return;

	// First grab all shadow textures we may want to render
	int nCount = s_VisibleShadowList.FindShadows( pView, leafCount, pLeafList );
	if (nCount == 0)
		return;

	m_nMaxModelsRendered = r_shadowmaxrendered.GetInt();

	// FIXME: Add heuristics based on distance, etc. to futz with
	// the shadow allocator + to select blobby shadows

	// We're starting the next frame
	m_ShadowAllocator.AdvanceFrame();

	// Save off the viewport...
	int x, y, w, h;
	materials->GetViewport( x, y, w, h );

	// Clear to white, black alpha
	materials->ClearColor4ub( 255, 255, 255, 0 );

	// No projection matrix (orthographic mode)
	// FIXME: Make it work for projective shadows?
	materials->MatrixMode( MATERIAL_PROJECTION );
	materials->PushMatrix();
	materials->LoadIdentity();
	materials->Scale( 1, -1, 1 );
	materials->Ortho( 0, 0, 1, 1, -9999, 0 );

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

	ITexture *pSaveRenderTarget = materials->GetRenderTarget();
	// GR - draw with disabled depth buffer
	materials->SetRenderTarget( m_ShadowAllocator.GetTexture() );

	ClearEntireRenderTarget();

	int nModelsRendered = 0;
	for (int i = 0; i < nCount; ++i)
	{
		const VisibleShadowInfo_t &info = s_VisibleShadowList.GetVisibleShadow(i);
		if ( (nModelsRendered < m_nMaxModelsRendered) ) // && !ShouldUseBlobbyShadows( info.m_flRadius, info.m_flArea ))
		{
			if (DrawRenderToTextureShadow( info.m_hShadow, info.m_flArea ))
			{
				++nModelsRendered;
			}
		}
		else
		{
			DrawRenderToTextureShadowLOD( info.m_hShadow );
		}
	}

	// Render to the backbuffer again
	materials->SetRenderTarget( pSaveRenderTarget );

	// Restore the matrices
	materials->MatrixMode( MATERIAL_PROJECTION );
	materials->PopMatrix();

	materials->MatrixMode( MATERIAL_VIEW );
	materials->PopMatrix();

	// Restore the viewport
	materials->Viewport( x, y, w, h );

	// Restore the clear color
	materials->ClearColor3ub( 0, 0, 0 );
}



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

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 )pProxyData;
	ITexture* pTex = s_ClientShadowMgr.GetShadowTexture( clientShadowHandle );
	if ((m_BaseTextureVar->GetType() != MATERIAL_VAR_TYPE_TEXTURE) || 
		(pTex != m_BaseTextureVar->GetTextureValue()))
	{
		m_BaseTextureVar->SetTextureValue( pTex );
	}
}

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; }

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 )pProxyData;
	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 );
}

EXPOSE_INTERFACE( CShadowModelProxy, IMaterialProxy, "ShadowModel" IMATERIAL_PROXY_INTERFACE_VERSION );




   


   