//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//===========================================================================//

#define DISABLE_PROTECTED_THINGS
#include "togl/rendermechanism.h"
#include "shadershadowdx8.h"
#include "locald3dtypes.h"
#include "utlvector.h"
#include "shaderapi/ishaderutil.h"
#include "shaderapidx8_global.h"
#include "shaderapidx8.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "materialsystem/imaterialsystem.h"
#include "imeshdx8.h"
#include "materialsystem/materialsystem_config.h"
#include "vertexshaderdx8.h"

// NOTE: This must be the last file included!
#include "tier0/memdbgon.h"


//-----------------------------------------------------------------------------
// The DX8 implementation of the shader setup interface
//-----------------------------------------------------------------------------
class CShaderShadowDX8 : public IShaderShadowDX8
{
public:
	// constructor, destructor
	CShaderShadowDX8( );
	virtual ~CShaderShadowDX8();

	// Initialize render state
	void Init( );

	// Sets the default state
	void SetDefaultState();

	// Methods related to depth buffering
	void DepthFunc( ShaderDepthFunc_t depthFunc );
	void EnableDepthWrites( bool bEnable );
	void EnableDepthTest( bool bEnable );
	void EnablePolyOffset( PolygonOffsetMode_t nOffsetMode );

	// Methods related to stencil. obsolete
	virtual void EnableStencil( bool bEnable )
	{
	}
	virtual void StencilFunc( ShaderStencilFunc_t stencilFunc )
	{
	}
	virtual void StencilPassOp( ShaderStencilOp_t stencilOp )
	{
	}
	virtual void StencilFailOp( ShaderStencilOp_t stencilOp )
	{
	}
	virtual void StencilDepthFailOp( ShaderStencilOp_t stencilOp )
	{
	}
	virtual void StencilReference( int nReference )
	{
	}
	virtual void StencilMask( int nMask )
	{
	}
	virtual void StencilWriteMask( int nMask )
	{
	}

	// Suppresses/activates color writing 
	void EnableColorWrites( bool bEnable );
	void EnableAlphaWrites( bool bEnable );

	// Methods related to alpha blending
	void EnableBlending( bool bEnable );

	void BlendFunc( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor );
	void BlendOp( ShaderBlendOp_t blendOp );
	void BlendOpSeparateAlpha( ShaderBlendOp_t blendOp );

	// Alpha testing
	void EnableAlphaTest( bool bEnable );
	void AlphaFunc( ShaderAlphaFunc_t alphaFunc, float alphaRef /* [0-1] */ );

	// Wireframe/filled polygons
	void PolyMode( ShaderPolyModeFace_t face, ShaderPolyMode_t polyMode );

	// Back face culling
	void EnableCulling( bool bEnable );
	
	// constant color
	void EnableConstantColor( bool bEnable );

	// Indicates we're going to light the model
	void EnableLighting( bool bEnable );

	// Indicates specular lighting is going to be used
	void EnableSpecular( bool bEnable );

	// Convert from linear to gamma color space on writes to frame buffer.
	void EnableSRGBWrite( bool bEnable );

	// Convert from gamma to linear on texture fetch.
	void EnableSRGBRead( Sampler_t stage, bool bEnable );

	// Set up appropriate shadow filtering state (such as Fetch4 on ATI)
	void SetShadowDepthFiltering( Sampler_t stage );

	// Computes the vertex format
	virtual void VertexShaderVertexFormat( unsigned int nFlags, 
		int nTexCoordCount, int* pTexCoordDimensions, int nUserDataSize );

	// Pixel and vertex shader methods
	virtual void SetVertexShader( const char* pFileName, int nStaticVshIndex );
	virtual	void SetPixelShader( const char* pFileName, int nStaticPshIndex );

	// Indicates we're going to be using the ambient cube
	void EnableAmbientLightCubeOnStage0( bool bEnable );

	// Activate/deactivate skinning
	void EnableVertexBlend( bool bEnable );

	// per texture unit stuff
	void OverbrightValue( TextureStage_t stage, float value );
	void EnableTexture( Sampler_t stage, bool bEnable );
	void EnableTexGen( TextureStage_t stage, bool bEnable );
	void TexGen( TextureStage_t stage, ShaderTexGenParam_t param );
	void TextureCoordinate( TextureStage_t stage, int useCoord );

	// alternate method of specifying per-texture unit stuff, more flexible and more complicated
	// Can be used to specify different operation per channel (alpha/color)...
	void EnableCustomPixelPipe( bool bEnable );
	void CustomTextureStages( int stageCount );
	void CustomTextureOperation( TextureStage_t stage, ShaderTexChannel_t channel,
		ShaderTexOp_t op, ShaderTexArg_t arg1, ShaderTexArg_t arg2 );

	// A simpler method of dealing with alpha modulation
	void EnableAlphaPipe( bool bEnable );
	void EnableConstantAlpha( bool bEnable );
	void EnableVertexAlpha( bool bEnable );
	void EnableTextureAlpha( TextureStage_t stage, bool bEnable );

	// helper functions
	void EnableSphereMapping( TextureStage_t stage, bool bEnable );

	// Last call to be make before snapshotting
	void ComputeAggregateShadowState( );

	// Gets at the shadow state
	const ShadowState_t & GetShadowState();
	const ShadowShaderState_t & GetShadowShaderState();

	// GR - Separate alpha blending
	void EnableBlendingSeparateAlpha( bool bEnable );
	void BlendFuncSeparateAlpha( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor );

	void FogMode( ShaderFogMode_t fogMode );
	void DisableFogGammaCorrection( bool bDisable );

	void SetDiffuseMaterialSource( ShaderMaterialSource_t materialSource );
	virtual void SetMorphFormat( MorphFormat_t flags );

	// Alpha to coverage
	void EnableAlphaToCoverage( bool bEnable );

private:
	struct TextureStageState_t
	{
		int				m_TexCoordIndex;
		int				m_TexCoordinate;
		float			m_OverbrightVal;
		ShaderTexArg_t	m_Arg[2][2];
		ShaderTexOp_t	m_Op[2];
		unsigned char	m_TexGenEnable:1;
		unsigned char	m_TextureAlphaEnable:1;
	};

	struct SamplerState_t
	{
		bool m_TextureEnable : 1;
	};

	// Computes the blend factor
	D3DBLEND BlendFuncValue( ShaderBlendFactor_t factor ) const;

	// Computes the blend op
	D3DBLENDOP BlendOpValue( ShaderBlendOp_t blendOp ) const;

	// Configures the FVF vertex shader
	void ConfigureFVFVertexShader( unsigned int flags );
	void ConfigureCustomFVFVertexShader( unsigned int flags );

	// Configures our texture indices
	void ConfigureTextureCoordinates( unsigned int flags );

	// Returns a blend value based on overbrighting
	D3DTEXTUREOP OverbrightBlendValue( TextureStage_t stage );

	// Sets the desired color and alpha op state
	void DrawFlags( unsigned int flags );

	// Computes a vertex format for the draw flags
	VertexFormat_t FlagsToVertexFormat( int flags ) const;

	// Indicates we've got a constant color specified
	bool HasConstantColor() const;

	// Configures the alpha pipe
	void ConfigureAlphaPipe( unsigned int flags );

	// returns true if we're using texture coordinates at a given stage
	bool IsUsingTextureCoordinates( Sampler_t stage ) const;

	// Recomputes the tex coord index
	void RecomputeTexCoordIndex( TextureStage_t stage );


	// State needed to create the snapshots
	IMaterialSystemHardwareConfig* m_pHardwareConfig;
	
	// Separate alpha control?
	bool		m_AlphaPipe;

	// Constant color state
	bool		m_HasConstantColor;
	bool		m_HasConstantAlpha;

	// Vertex color state
	bool		m_HasVertexAlpha;

	// funky custom method of specifying shader state
	bool		m_CustomTextureStageState;

	// Number of stages used by the custom pipeline
	int			m_CustomTextureStages;

	// Number of bones...
	int			m_NumBlendVertices;

	// Draw flags
	int			m_DrawFlags;

	// Alpha blending...
	D3DBLEND	m_SrcBlend;
	D3DBLEND	m_DestBlend;
	D3DBLENDOP	m_BlendOp;

	// GR - Separate alpha blending...
	D3DBLEND	m_SrcBlendAlpha;
	D3DBLEND	m_DestBlendAlpha;
	D3DBLENDOP	m_BlendOpAlpha;

	// Alpha testing
	D3DCMPFUNC	m_AlphaFunc;
	int			m_AlphaRef;

	// Stencil
	D3DCMPFUNC	m_StencilFunc;
	int			m_StencilRef;
	int			m_StencilMask;
	DWORD		m_StencilFail;
	DWORD		m_StencilZFail;
	DWORD		m_StencilPass;
	int			m_StencilWriteMask;

	// The current shadow state
	ShadowState_t m_ShadowState;
	ShadowShaderState_t m_ShadowShaderState;

	// State info stores with each texture stage
	TextureStageState_t m_TextureStage[MAX_TEXTURE_STAGES];
	SamplerState_t m_SamplerState[MAX_SAMPLERS];
};


//-----------------------------------------------------------------------------
// Class factory
//-----------------------------------------------------------------------------
static CShaderShadowDX8 g_ShaderShadow;
IShaderShadowDX8 *g_pShaderShadowDx8 = &g_ShaderShadow;

EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CShaderShadowDX8, IShaderShadow, 
								  SHADERSHADOW_INTERFACE_VERSION, g_ShaderShadow )

//-----------------------------------------------------------------------------
// Global instance
//-----------------------------------------------------------------------------
IShaderShadowDX8* ShaderShadow()
{
	return &g_ShaderShadow;
}


//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CShaderShadowDX8::CShaderShadowDX8( ) :
	m_DrawFlags(0), m_pHardwareConfig(0), m_HasConstantColor(false)
{	
	memset( &m_ShadowState, 0, sizeof(m_ShadowState) );
	memset( &m_TextureStage, 0, sizeof(m_TextureStage) );
}

CShaderShadowDX8::~CShaderShadowDX8()
{
}


//-----------------------------------------------------------------------------
// Initialize render state
//-----------------------------------------------------------------------------
void CShaderShadowDX8::Init( )
{
	m_pHardwareConfig = HardwareConfig();
	
	// Clear out the shadow state
	memset( &m_ShadowState, 0, sizeof(m_ShadowState) );

	// No funky custom methods..
	m_CustomTextureStageState = false;

	// No constant color modulation
	m_HasConstantColor = false;
	m_HasConstantAlpha = false;
	m_HasVertexAlpha = false;

	m_ShadowShaderState.m_ModulateConstantColor = false;

	m_ShadowState.m_bDisableFogGammaCorrection = false;

	// By default we're using fixed function
	m_ShadowState.m_UsingFixedFunction = true;

	// Lighting off by default
	m_ShadowState.m_Lighting = false;

	// Pixel + vertex shaders
	m_ShadowShaderState.m_VertexShader = INVALID_SHADER;
	m_ShadowShaderState.m_PixelShader = INVALID_SHADER;
	m_ShadowShaderState.m_nStaticPshIndex = 0;
	m_ShadowShaderState.m_nStaticVshIndex = 0;
	m_ShadowShaderState.m_VertexUsage = 0;

	// Drawing nothing..
	m_DrawFlags = 0;

	// No alpha control
	m_AlphaPipe = false;

	// Vertex blending
	m_NumBlendVertices = 0;
	m_ShadowState.m_VertexBlendEnable = false;

	// NOTE: If you change these defaults, change the code in ComputeAggregateShadowState + CreateTransitionTableEntry
	int i;
	for (i = 0; i < MAX_TEXTURE_STAGES; ++i)
	{
		m_ShadowState.m_TextureStage[i].m_ColorOp	= D3DTOP_DISABLE;
		m_ShadowState.m_TextureStage[i].m_ColorArg1 = D3DTA_TEXTURE;
		m_ShadowState.m_TextureStage[i].m_ColorArg2 = (i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT;
		m_ShadowState.m_TextureStage[i].m_AlphaOp	= D3DTOP_DISABLE;
		m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE;
		m_ShadowState.m_TextureStage[i].m_AlphaArg2 = (i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT;
		m_ShadowState.m_TextureStage[i].m_TexCoordIndex = i;
	}

	for (i = 0; i < MAX_SAMPLERS; ++i)
	{
		m_ShadowState.m_SamplerState[i].m_TextureEnable = false;
		m_ShadowState.m_SamplerState[i].m_SRGBReadEnable = false;
		m_ShadowState.m_SamplerState[i].m_Fetch4Enable = false;
#ifdef DX_TO_GL_ABSTRACTION
		m_ShadowState.m_SamplerState[i].m_ShadowFilterEnable = false;
#endif
		// A *real* measure if the texture stage is being used.
		// we sometimes have to set the shadow state to not mirror this.
		m_SamplerState[i].m_TextureEnable = false;
	}
}


//-----------------------------------------------------------------------------
// Sets the default state
//-----------------------------------------------------------------------------
void CShaderShadowDX8::SetDefaultState()
{
	DepthFunc( SHADER_DEPTHFUNC_NEAREROREQUAL );
	EnableDepthWrites( true );
	EnableDepthTest( true );
	EnableColorWrites( true );
	EnableAlphaWrites( false );
	EnableAlphaTest( false );
	EnableLighting( false );
	EnableConstantColor( false );
	EnableBlending( false );
	BlendFunc( SHADER_BLEND_ONE, SHADER_BLEND_ZERO );
	BlendOp( SHADER_BLEND_OP_ADD );
	// GR - separate alpha
	EnableBlendingSeparateAlpha( false );
	BlendFuncSeparateAlpha( SHADER_BLEND_ONE, SHADER_BLEND_ZERO );
	BlendOpSeparateAlpha( SHADER_BLEND_OP_ADD );
	AlphaFunc( SHADER_ALPHAFUNC_GEQUAL, 0.7f );
	PolyMode( SHADER_POLYMODEFACE_FRONT_AND_BACK, SHADER_POLYMODE_FILL );
	EnableCulling( true );
	EnableAlphaToCoverage( false );
	EnablePolyOffset( SHADER_POLYOFFSET_DISABLE );
	EnableVertexBlend( false );
	EnableSpecular( false );
	EnableSRGBWrite( false );
	DrawFlags( SHADER_DRAW_POSITION );
	EnableCustomPixelPipe( false );
	CustomTextureStages( 0 );
	EnableAlphaPipe( false );
	EnableConstantAlpha( false );
	EnableVertexAlpha( false );
	SetVertexShader( NULL, 0 );
	SetPixelShader( NULL, 0 );
	FogMode( SHADER_FOGMODE_DISABLED );
	DisableFogGammaCorrection( false );
	SetDiffuseMaterialSource( SHADER_MATERIALSOURCE_MATERIAL );
	EnableStencil( false );
	StencilFunc( SHADER_STENCILFUNC_ALWAYS );
	StencilPassOp( SHADER_STENCILOP_KEEP );
	StencilFailOp( SHADER_STENCILOP_KEEP );
	StencilDepthFailOp( SHADER_STENCILOP_KEEP );
	StencilReference( 0 );
	StencilMask( 0xFFFFFFFF );
	StencilWriteMask( 0xFFFFFFFF );
	m_ShadowShaderState.m_VertexUsage = 0;

	int i;
	int nSamplerCount = HardwareConfig()->GetSamplerCount();
	for( i = 0; i < nSamplerCount; i++ )
	{
		EnableTexture( (Sampler_t)i, false );
		EnableSRGBRead( (Sampler_t)i, false );
	}

	int nTextureStageCount = HardwareConfig()->GetTextureStageCount();
	for( i = 0; i < nTextureStageCount; i++ )
	{
		EnableTexGen( (TextureStage_t)i, false );
		OverbrightValue( (TextureStage_t)i, 1.0f );
		EnableTextureAlpha( (TextureStage_t)i, false );
		CustomTextureOperation( (TextureStage_t)i, SHADER_TEXCHANNEL_COLOR, 
			SHADER_TEXOP_DISABLE, SHADER_TEXARG_TEXTURE, SHADER_TEXARG_PREVIOUSSTAGE );
		CustomTextureOperation( (TextureStage_t)i, SHADER_TEXCHANNEL_ALPHA, 
			SHADER_TEXOP_DISABLE, SHADER_TEXARG_TEXTURE, SHADER_TEXARG_PREVIOUSSTAGE );
	}
}


//-----------------------------------------------------------------------------
// Gets at the shadow state
//-----------------------------------------------------------------------------
const ShadowState_t &CShaderShadowDX8::GetShadowState()
{
	return m_ShadowState;
}

const ShadowShaderState_t &CShaderShadowDX8::GetShadowShaderState()
{
	return m_ShadowShaderState;
}


//-----------------------------------------------------------------------------
// Depth functions...
//-----------------------------------------------------------------------------
void CShaderShadowDX8::DepthFunc( ShaderDepthFunc_t depthFunc )
{
	D3DCMPFUNC zFunc;

	switch( depthFunc )
	{
	case SHADER_DEPTHFUNC_NEVER:
		zFunc = D3DCMP_NEVER;
		break;
	case SHADER_DEPTHFUNC_NEARER:
		zFunc = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? D3DCMP_GREATER : D3DCMP_LESS;
		break;
	case SHADER_DEPTHFUNC_EQUAL:
		zFunc = D3DCMP_EQUAL;
		break;
	case SHADER_DEPTHFUNC_NEAREROREQUAL:
		zFunc = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? D3DCMP_GREATEREQUAL : D3DCMP_LESSEQUAL;
		break;
	case SHADER_DEPTHFUNC_FARTHER:
		zFunc = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? D3DCMP_LESS : D3DCMP_GREATER;
		break;
	case SHADER_DEPTHFUNC_NOTEQUAL:
		zFunc = D3DCMP_NOTEQUAL;
		break;
	case SHADER_DEPTHFUNC_FARTHEROREQUAL:
		zFunc = (ShaderUtil()->GetConfig().bReverseDepth ^ ReverseDepthOnX360()) ? D3DCMP_LESSEQUAL : D3DCMP_GREATEREQUAL;
		break;
	case SHADER_DEPTHFUNC_ALWAYS:
		zFunc = D3DCMP_ALWAYS;
		break;
	default:
		zFunc = D3DCMP_ALWAYS;
		Warning( "DepthFunc: invalid param\n" );
		break;
	}

	m_ShadowState.m_ZFunc = zFunc;
}

void CShaderShadowDX8::EnableDepthWrites( bool bEnable )
{
	m_ShadowState.m_ZWriteEnable = bEnable;
}

void CShaderShadowDX8::EnableDepthTest( bool bEnable )
{
	m_ShadowState.m_ZEnable = bEnable ? D3DZB_TRUE : D3DZB_FALSE;
}

void CShaderShadowDX8::EnablePolyOffset( PolygonOffsetMode_t nOffsetMode )
{
	m_ShadowState.m_ZBias = nOffsetMode;
}

//-----------------------------------------------------------------------------
// Color write state
//-----------------------------------------------------------------------------
void CShaderShadowDX8::EnableColorWrites( bool bEnable )
{
	if (bEnable)
	{
		m_ShadowState.m_ColorWriteEnable |= D3DCOLORWRITEENABLE_BLUE |
							D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED;
	}
	else
	{
		m_ShadowState.m_ColorWriteEnable &= ~( D3DCOLORWRITEENABLE_BLUE |
							D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED );
	}
}

void CShaderShadowDX8::EnableAlphaWrites( bool bEnable )
{
	if (bEnable)
	{
		m_ShadowState.m_ColorWriteEnable |= D3DCOLORWRITEENABLE_ALPHA;
	}
	else
	{
		m_ShadowState.m_ColorWriteEnable &= ~D3DCOLORWRITEENABLE_ALPHA;
	}
}


//-----------------------------------------------------------------------------
// Alpha blending states
//-----------------------------------------------------------------------------
void CShaderShadowDX8::EnableBlending( bool bEnable )
{
	m_ShadowState.m_AlphaBlendEnable = bEnable;
}

// GR - separate alpha
void CShaderShadowDX8::EnableBlendingSeparateAlpha( bool bEnable )
{
	m_ShadowState.m_SeparateAlphaBlendEnable = bEnable;
}

void CShaderShadowDX8::EnableAlphaTest( bool bEnable )
{
	m_ShadowState.m_AlphaTestEnable = bEnable;
}

void CShaderShadowDX8::AlphaFunc( ShaderAlphaFunc_t alphaFunc, float alphaRef /* [0-1] */ )
{
	D3DCMPFUNC d3dCmpFunc;

	switch( alphaFunc )
	{
	case SHADER_ALPHAFUNC_NEVER:
		d3dCmpFunc = D3DCMP_NEVER;
		break;
	case SHADER_ALPHAFUNC_LESS:
		d3dCmpFunc = D3DCMP_LESS;
		break;
	case SHADER_ALPHAFUNC_EQUAL:
		d3dCmpFunc = D3DCMP_EQUAL;
		break;
	case SHADER_ALPHAFUNC_LEQUAL:
		d3dCmpFunc = D3DCMP_LESSEQUAL;
		break;
	case SHADER_ALPHAFUNC_GREATER:
		d3dCmpFunc = D3DCMP_GREATER;
		break;
	case SHADER_ALPHAFUNC_NOTEQUAL:
		d3dCmpFunc = D3DCMP_NOTEQUAL;
		break;
	case SHADER_ALPHAFUNC_GEQUAL:
		d3dCmpFunc = D3DCMP_GREATEREQUAL;
		break;
	case SHADER_ALPHAFUNC_ALWAYS:
		d3dCmpFunc = D3DCMP_ALWAYS;
		break;
	default:
		Warning( "AlphaFunc: invalid param\n" );
		return;
	}

	m_AlphaFunc = d3dCmpFunc;
	m_AlphaRef = (int)(alphaRef * 255);
}

D3DBLEND CShaderShadowDX8::BlendFuncValue( ShaderBlendFactor_t factor ) const
{
	switch( factor )
	{
	case SHADER_BLEND_ZERO:
		return D3DBLEND_ZERO;

	case SHADER_BLEND_ONE:
		return D3DBLEND_ONE;

	case SHADER_BLEND_DST_COLOR:
		return D3DBLEND_DESTCOLOR;

	case SHADER_BLEND_ONE_MINUS_DST_COLOR:
		return D3DBLEND_INVDESTCOLOR;

	case SHADER_BLEND_SRC_ALPHA:
		return D3DBLEND_SRCALPHA;

	case SHADER_BLEND_ONE_MINUS_SRC_ALPHA:
		return D3DBLEND_INVSRCALPHA;

	case SHADER_BLEND_DST_ALPHA:
		return D3DBLEND_DESTALPHA;

	case SHADER_BLEND_ONE_MINUS_DST_ALPHA:
		return D3DBLEND_INVDESTALPHA;

	case SHADER_BLEND_SRC_ALPHA_SATURATE:
		return D3DBLEND_SRCALPHASAT;

	case SHADER_BLEND_SRC_COLOR:
		return D3DBLEND_SRCCOLOR;

	case SHADER_BLEND_ONE_MINUS_SRC_COLOR:
		return D3DBLEND_INVSRCCOLOR;
	}

	Warning( "BlendFunc: invalid factor\n" );
	return D3DBLEND_ONE;
}

D3DBLENDOP CShaderShadowDX8::BlendOpValue( ShaderBlendOp_t blendOp ) const
{
	switch( blendOp )
	{
	case SHADER_BLEND_OP_ADD:
		return D3DBLENDOP_ADD;

	case SHADER_BLEND_OP_SUBTRACT:
		return D3DBLENDOP_SUBTRACT;

	case SHADER_BLEND_OP_REVSUBTRACT:
		return D3DBLENDOP_REVSUBTRACT;

	case SHADER_BLEND_OP_MIN:
		return D3DBLENDOP_MIN;

	case SHADER_BLEND_OP_MAX:
		return D3DBLENDOP_MAX;
	}

	Warning( "BlendOp: invalid op\n" );
	return D3DBLENDOP_ADD;
}

void CShaderShadowDX8::BlendFunc( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor )
{
	D3DBLEND d3dSrcFactor = BlendFuncValue( srcFactor );
	D3DBLEND d3dDstFactor = BlendFuncValue( dstFactor );
	m_SrcBlend = d3dSrcFactor;
	m_DestBlend = d3dDstFactor;
}

// GR - separate alpha blend
void CShaderShadowDX8::BlendFuncSeparateAlpha( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor )
{
	D3DBLEND d3dSrcFactor = BlendFuncValue( srcFactor );
	D3DBLEND d3dDstFactor = BlendFuncValue( dstFactor );
	m_SrcBlendAlpha = d3dSrcFactor;
	m_DestBlendAlpha = d3dDstFactor;
}

void CShaderShadowDX8::BlendOp( ShaderBlendOp_t blendOp )
{
	m_BlendOp = BlendOpValue( blendOp );
}

void CShaderShadowDX8::BlendOpSeparateAlpha( ShaderBlendOp_t blendOp )
{
	m_BlendOpAlpha = BlendOpValue( blendOp );
}

//-----------------------------------------------------------------------------
// Polygon fill mode states
//-----------------------------------------------------------------------------
void CShaderShadowDX8::PolyMode( ShaderPolyModeFace_t face, ShaderPolyMode_t polyMode )
{
	// DX8 can't handle different modes on front and back faces
// FIXME:	Assert( face == SHADER_POLYMODEFACE_FRONT_AND_BACK );
	if (face == SHADER_POLYMODEFACE_BACK)
		return;
	
	D3DFILLMODE fillMode;
	switch( polyMode )
	{
	case SHADER_POLYMODE_POINT:
		fillMode = D3DFILL_POINT;
		break;
	case SHADER_POLYMODE_LINE:
		fillMode =  D3DFILL_WIREFRAME;
		break;
	case SHADER_POLYMODE_FILL:
		fillMode =  D3DFILL_SOLID;
		break;
	default:
		Warning( "PolyMode: invalid poly mode\n" );
		return;
	}

	m_ShadowState.m_FillMode = fillMode;
}


//-----------------------------------------------------------------------------
// Backface cull states
//-----------------------------------------------------------------------------
void CShaderShadowDX8::EnableCulling( bool bEnable )
{
	m_ShadowState.m_CullEnable = bEnable;
}


//-----------------------------------------------------------------------------
// Alpha to coverage
//-----------------------------------------------------------------------------
void CShaderShadowDX8::EnableAlphaToCoverage( bool bEnable )
{
	m_ShadowState.m_EnableAlphaToCoverage = bEnable;
}


//-----------------------------------------------------------------------------
// Indicates we've got a constant color specified
//-----------------------------------------------------------------------------
bool CShaderShadowDX8::HasConstantColor() const
{
	return m_HasConstantColor;
}

void CShaderShadowDX8::EnableConstantColor( bool bEnable ) 
{
	m_HasConstantColor = bEnable;
}


//-----------------------------------------------------------------------------
// A simpler method of dealing with alpha modulation
//-----------------------------------------------------------------------------
void CShaderShadowDX8::EnableAlphaPipe( bool bEnable )
{
	m_AlphaPipe = bEnable;
}

void CShaderShadowDX8::EnableConstantAlpha( bool bEnable )
{
	m_HasConstantAlpha = bEnable;
}

void CShaderShadowDX8::EnableVertexAlpha( bool bEnable )
{
	m_HasVertexAlpha = bEnable;
}

void CShaderShadowDX8::EnableTextureAlpha( TextureStage_t stage, bool bEnable )
{
	if ( stage < m_pHardwareConfig->GetSamplerCount() )
	{
		m_TextureStage[stage].m_TextureAlphaEnable = bEnable;
	}
}


//-----------------------------------------------------------------------------
// Indicates we're going to light the model
//-----------------------------------------------------------------------------
void CShaderShadowDX8::EnableLighting( bool bEnable )
{
	m_ShadowState.m_Lighting = bEnable;
}


//-----------------------------------------------------------------------------
// Enables specular lighting (lighting has also got to be enabled)
//-----------------------------------------------------------------------------
void CShaderShadowDX8::EnableSpecular( bool bEnable )
{
	m_ShadowState.m_SpecularEnable = bEnable;
}

//-----------------------------------------------------------------------------
// Enables auto-conversion from linear to gamma space on write to framebuffer.
//-----------------------------------------------------------------------------
void CShaderShadowDX8::EnableSRGBWrite( bool bEnable )
{
	if ( m_pHardwareConfig->SupportsSRGB() )
	{
		m_ShadowState.m_SRGBWriteEnable = bEnable;
	}
	else
	{
		m_ShadowState.m_SRGBWriteEnable = false;
	}
}

//-----------------------------------------------------------------------------
// Activate/deactivate skinning
//-----------------------------------------------------------------------------
void CShaderShadowDX8::EnableVertexBlend( bool bEnable )
{
	// Activate/deactivate skinning. Indexed blending is automatically
	// enabled if it's available for this hardware. When blending is enabled,
	// we allocate enough room for 3 weights (max allowed)
	if ((m_pHardwareConfig->MaxBlendMatrices() > 0) || (!bEnable))
	{
		m_ShadowState.m_VertexBlendEnable = bEnable;
	}
}

				 
//-----------------------------------------------------------------------------
// Texturemapping state
//-----------------------------------------------------------------------------
void CShaderShadowDX8::EnableTexture( Sampler_t sampler, bool bEnable )
{
	if ( sampler < m_pHardwareConfig->GetSamplerCount() )
	{
		m_SamplerState[sampler].m_TextureEnable = bEnable;
	}
	else
	{
		Warning( "Attempting to bind a texture to an invalid sampler (%d)!\n", sampler );
	}
}

void CShaderShadowDX8::EnableSRGBRead( Sampler_t sampler, bool bEnable )
{
	if ( !m_pHardwareConfig->SupportsSRGB() )
	{
		m_ShadowState.m_SamplerState[sampler].m_SRGBReadEnable = false;
		return;
	}

	if ( sampler < m_pHardwareConfig->GetSamplerCount() )
	{
		m_ShadowState.m_SamplerState[sampler].m_SRGBReadEnable = bEnable;
	}
	else
	{
		Warning( "Attempting set SRGBRead state on an invalid sampler (%d)!\n", sampler );
	}
}

void CShaderShadowDX8::SetShadowDepthFiltering( Sampler_t stage )
{
#ifdef DX_TO_GL_ABSTRACTION
	if ( stage < m_pHardwareConfig->GetSamplerCount() )
	{
		m_ShadowState.m_SamplerState[stage].m_ShadowFilterEnable = true;
		return;
	}
#else
	if ( !m_pHardwareConfig->SupportsFetch4() )
	{
		m_ShadowState.m_SamplerState[stage].m_Fetch4Enable = false;
		return;
	}

	if ( stage < m_pHardwareConfig->GetSamplerCount() )
	{
		m_ShadowState.m_SamplerState[stage].m_Fetch4Enable = true;
		return;
	}
#endif
	
	Warning( "Attempting set shadow filtering state on an invalid sampler (%d)!\n", stage );
}


//-----------------------------------------------------------------------------
// Binds texture coordinates to a particular stage...
//-----------------------------------------------------------------------------
void CShaderShadowDX8::TextureCoordinate( TextureStage_t stage, int useTexCoord )
{
	if ( stage < m_pHardwareConfig->GetTextureStageCount() )
	{
		m_TextureStage[stage].m_TexCoordinate = useTexCoord;

		// Need to recompute the texCoordIndex, since that's affected by this
		RecomputeTexCoordIndex(stage);
	}
}


//-----------------------------------------------------------------------------
// Automatic texture coordinate generation
//-----------------------------------------------------------------------------
void CShaderShadowDX8::RecomputeTexCoordIndex( TextureStage_t stage )
{
	int texCoordIndex = m_TextureStage[stage].m_TexCoordinate;
	if (m_TextureStage[stage].m_TexGenEnable)
		texCoordIndex |= m_TextureStage[stage].m_TexCoordIndex;
	m_ShadowState.m_TextureStage[stage].m_TexCoordIndex = texCoordIndex;
}


//-----------------------------------------------------------------------------
// Automatic texture coordinate generation
//-----------------------------------------------------------------------------
void CShaderShadowDX8::EnableTexGen( TextureStage_t stage, bool bEnable )
{
	if ( stage >= m_pHardwareConfig->GetTextureStageCount() )
	{
		Assert( 0 );
		return;
	}

	m_TextureStage[stage].m_TexGenEnable = bEnable;
	RecomputeTexCoordIndex(stage);
}

//-----------------------------------------------------------------------------
// Automatic texture coordinate generation
//-----------------------------------------------------------------------------
void CShaderShadowDX8::TexGen( TextureStage_t stage, ShaderTexGenParam_t param )
{
#ifdef FIXED_FUNCTION_PIPELINE
	if ( stage >= m_pHardwareConfig->GetTextureStageCount() )
		return;

	switch( param )
	{
	case SHADER_TEXGENPARAM_OBJECT_LINEAR:
		m_TextureStage[stage].m_TexCoordIndex = 0;	
		break;	
	case SHADER_TEXGENPARAM_EYE_LINEAR:
		m_TextureStage[stage].m_TexCoordIndex = D3DTSS_TCI_CAMERASPACEPOSITION;
		break;
	case SHADER_TEXGENPARAM_SPHERE_MAP:
		if ( m_pHardwareConfig->SupportsSpheremapping() )
		{
			m_TextureStage[stage].m_TexCoordIndex = D3DTSS_TCI_SPHEREMAP;
		}
		else
		{
			m_TextureStage[stage].m_TexCoordIndex = D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR;
		}
		break;
	case SHADER_TEXGENPARAM_CAMERASPACEREFLECTIONVECTOR:
		m_TextureStage[stage].m_TexCoordIndex = D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR;
		break;
	case SHADER_TEXGENPARAM_CAMERASPACENORMAL:
		m_TextureStage[stage].m_TexCoordIndex = D3DTSS_TCI_CAMERASPACENORMAL;
		break;
	}

	// Set the board state...
	RecomputeTexCoordIndex(stage);
#endif
}


//-----------------------------------------------------------------------------
// Overbrighting
//-----------------------------------------------------------------------------
void CShaderShadowDX8::OverbrightValue( TextureStage_t stage, float value )
{
	if ( m_pHardwareConfig->SupportsOverbright() && 
		( stage < m_pHardwareConfig->GetTextureStageCount() ) )
	{
		m_TextureStage[stage].m_OverbrightVal = value;
	}
}




//-----------------------------------------------------------------------------
// alternate method of specifying per-texture unit stuff, more flexible and more complicated
// Can be used to specify different operation per channel (alpha/color)...
//-----------------------------------------------------------------------------
void CShaderShadowDX8::EnableCustomPixelPipe( bool bEnable )
{
	m_CustomTextureStageState = bEnable;
}

void CShaderShadowDX8::CustomTextureStages( int stageCount )
{
	m_CustomTextureStages = stageCount;
	Assert( stageCount <= m_pHardwareConfig->GetTextureStageCount() );
	if ( stageCount > m_pHardwareConfig->GetTextureStageCount() )
		stageCount = m_pHardwareConfig->GetTextureStageCount();
}

void CShaderShadowDX8::CustomTextureOperation( TextureStage_t stage, 
	ShaderTexChannel_t channel, ShaderTexOp_t op, ShaderTexArg_t arg1, ShaderTexArg_t arg2 )
{
	m_TextureStage[stage].m_Op[channel]= op;
	m_TextureStage[stage].m_Arg[channel][0] = arg1;
	m_TextureStage[stage].m_Arg[channel][1] = arg2;
}


//-----------------------------------------------------------------------------
// Compute the vertex format from vertex descriptor flags
//-----------------------------------------------------------------------------
void CShaderShadowDX8::VertexShaderVertexFormat( unsigned int nFlags, 
	int nTexCoordCount, int* pTexCoordDimensions, int nUserDataSize )
{
	// Code that creates a Mesh should specify whether it contains bone weights+indices, *not* the shader.
	Assert( ( nFlags & VERTEX_BONE_INDEX ) == 0 );
	nFlags &= ~VERTEX_BONE_INDEX;

	// This indicates we're using a vertex shader
	nFlags |= VERTEX_FORMAT_VERTEX_SHADER;
	m_ShadowShaderState.m_VertexUsage = MeshMgr()->ComputeVertexFormat( nFlags, nTexCoordCount, 
		pTexCoordDimensions, 0, nUserDataSize );
	m_ShadowState.m_UsingFixedFunction = false;

	// Avoid an error if vertex stream 0 is too narrow
	if ( CVertexBufferBase::VertexFormatSize( m_ShadowShaderState.m_VertexUsage ) <= 16 )
	{
		// FIXME: this is only necessary because we
		//          (a) put the flex normal/position stream in ALL vertex decls
		//          (b) bind stream 0's VB to stream 2 if there is no actual flex data
		//        ...it would be far more sensible to not add stream 2 to all vertex decls.
		static bool bComplained = false;
		if( !bComplained )
		{
			Warning( "ERROR: shader asking for a too-narrow vertex format - you will see errors if running with debug D3D DLLs!\n\tPadding the vertex format with extra texcoords\n\tWill not warn again.\n" );
			bComplained = true;
		}
		// All vertex formats should contain position...
		Assert( nFlags & VERTEX_POSITION );
		nFlags |= VERTEX_POSITION;
		// This error should occur only if we have zero texcoords, or if we have a single, 1-D texcoord
		Assert( ( nTexCoordCount == 0 ) ||
			    ( ( nTexCoordCount == 1 ) && pTexCoordDimensions && ( pTexCoordDimensions[0] == 1 ) ) );
		nTexCoordCount = 1;
		m_ShadowShaderState.m_VertexUsage = MeshMgr()->ComputeVertexFormat( nFlags, nTexCoordCount, NULL, 0, nUserDataSize );
	}
}


//-----------------------------------------------------------------------------
// Compute the vertex format from vertex descriptor flags
//-----------------------------------------------------------------------------
void CShaderShadowDX8::SetMorphFormat( MorphFormat_t flags )
{
	m_ShadowShaderState.m_MorphUsage = flags;
}

//-----------------------------------------------------------------------------
// Pixel and vertex shader methods
//-----------------------------------------------------------------------------
void CShaderShadowDX8::SetVertexShader( const char* pFileName, int nStaticVshIndex )
{
	char debugLabel[500] = "";
#ifdef DX_TO_GL_ABSTRACTION
	Q_snprintf( debugLabel, sizeof(debugLabel), "vs-file %s vs-index %d", pFileName, nStaticVshIndex ); 
#endif

	m_ShadowShaderState.m_VertexShader = ShaderManager()->CreateVertexShader( pFileName, nStaticVshIndex, debugLabel );
	m_ShadowShaderState.m_nStaticVshIndex = nStaticVshIndex;
}

void CShaderShadowDX8::SetPixelShader( const char* pFileName, int nStaticPshIndex )
{
	char debugLabel[500] = "";
#ifdef DX_TO_GL_ABSTRACTION
	Q_snprintf( debugLabel, sizeof(debugLabel), "ps-file %s ps-index %d", pFileName, nStaticPshIndex ); 
#endif

	m_ShadowShaderState.m_PixelShader = ShaderManager()->CreatePixelShader( pFileName, nStaticPshIndex, debugLabel );
	m_ShadowShaderState.m_nStaticPshIndex = nStaticPshIndex;
}


//-----------------------------------------------------------------------------
// NOTE: See Version 5 of this file for NVidia 8-stage shader stuff
//-----------------------------------------------------------------------------
inline bool CShaderShadowDX8::IsUsingTextureCoordinates( Sampler_t sampler ) const
{
	return m_SamplerState[sampler].m_TextureEnable;
}

inline D3DTEXTUREOP CShaderShadowDX8::OverbrightBlendValue( TextureStage_t stage )
{
	D3DTEXTUREOP colorop;
	if (m_TextureStage[stage].m_OverbrightVal < 2.0F)
		colorop = D3DTOP_MODULATE;
	else if (m_TextureStage[stage].m_OverbrightVal < 4.0F)
		colorop = D3DTOP_MODULATE2X;
	else
		colorop = D3DTOP_MODULATE4X;
	return colorop;
}

static inline int ComputeArg( ShaderTexArg_t arg )
{
	switch(arg)
	{
	case SHADER_TEXARG_TEXTURE:
		return D3DTA_TEXTURE;

	case SHADER_TEXARG_ZERO:
		return D3DTA_SPECULAR | D3DTA_COMPLEMENT;

	case SHADER_TEXARG_ONE:
		return D3DTA_SPECULAR;

	case SHADER_TEXARG_TEXTUREALPHA:
		return D3DTA_TEXTURE | D3DTA_ALPHAREPLICATE;

	case SHADER_TEXARG_INVTEXTUREALPHA:
		return D3DTA_TEXTURE | D3DTA_ALPHAREPLICATE | D3DTA_COMPLEMENT;

	case SHADER_TEXARG_NONE:
	case SHADER_TEXARG_VERTEXCOLOR:
		return D3DTA_DIFFUSE;

	case SHADER_TEXARG_SPECULARCOLOR:
		return D3DTA_SPECULAR;

	case SHADER_TEXARG_CONSTANTCOLOR:
		return D3DTA_TFACTOR;

	case SHADER_TEXARG_PREVIOUSSTAGE:
		return D3DTA_CURRENT;
	}

	Assert(0);
	return D3DTA_TEXTURE;
}

static inline D3DTEXTUREOP ComputeOp( ShaderTexOp_t op )
{
	switch(op)
	{
	case SHADER_TEXOP_MODULATE:
		return D3DTOP_MODULATE;

	case SHADER_TEXOP_MODULATE2X:
		return D3DTOP_MODULATE2X;

	case SHADER_TEXOP_MODULATE4X:
		return D3DTOP_MODULATE4X;

	case SHADER_TEXOP_SELECTARG1:
		return D3DTOP_SELECTARG1;

	case SHADER_TEXOP_SELECTARG2:
		return D3DTOP_SELECTARG2;

	case SHADER_TEXOP_ADD:
		return D3DTOP_ADD;

	case SHADER_TEXOP_SUBTRACT:
		return D3DTOP_SUBTRACT;

	case SHADER_TEXOP_ADDSIGNED2X:
		return D3DTOP_ADDSIGNED2X;

	case SHADER_TEXOP_BLEND_CONSTANTALPHA:
		return D3DTOP_BLENDFACTORALPHA;

	case SHADER_TEXOP_BLEND_PREVIOUSSTAGEALPHA:
		return D3DTOP_BLENDCURRENTALPHA;

	case SHADER_TEXOP_BLEND_TEXTUREALPHA:
		return D3DTOP_BLENDTEXTUREALPHA;
	
	case SHADER_TEXOP_MODULATECOLOR_ADDALPHA:
		return D3DTOP_MODULATECOLOR_ADDALPHA;

	case SHADER_TEXOP_MODULATEINVCOLOR_ADDALPHA:
		return D3DTOP_MODULATEINVCOLOR_ADDALPHA;

	case SHADER_TEXOP_DOTPRODUCT3:
		return D3DTOP_DOTPRODUCT3;

	case SHADER_TEXOP_DISABLE:
		return D3DTOP_DISABLE;
	}

	Assert(0);
	return D3DTOP_MODULATE;
}

void CShaderShadowDX8::ConfigureCustomFVFVertexShader( unsigned int flags )
{
	int i;
	for ( i = 0; i < m_CustomTextureStages; ++i)
	{
		m_ShadowState.m_TextureStage[i].m_ColorArg1 = ComputeArg( m_TextureStage[i].m_Arg[0][0] );
		m_ShadowState.m_TextureStage[i].m_ColorArg2 = ComputeArg( m_TextureStage[i].m_Arg[0][1] );								
		m_ShadowState.m_TextureStage[i].m_AlphaArg1 = ComputeArg( m_TextureStage[i].m_Arg[1][0] );
		m_ShadowState.m_TextureStage[i].m_AlphaArg2 = ComputeArg( m_TextureStage[i].m_Arg[1][1] );
 		m_ShadowState.m_TextureStage[i].m_ColorOp = ComputeOp( m_TextureStage[i].m_Op[0] );
		m_ShadowState.m_TextureStage[i].m_AlphaOp = ComputeOp( m_TextureStage[i].m_Op[1] );
	}

	// Deal with texture stage 1 -> n
	for ( i = m_CustomTextureStages; i < m_pHardwareConfig->GetTextureStageCount(); ++i )
	{
		m_ShadowState.m_TextureStage[i].m_ColorArg1 = D3DTA_TEXTURE;
		m_ShadowState.m_TextureStage[i].m_ColorArg2 = D3DTA_CURRENT;
		m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE;
		m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_CURRENT;
 		m_ShadowState.m_TextureStage[i].m_ColorOp = D3DTOP_DISABLE;
		m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_DISABLE;
	}
}


//-----------------------------------------------------------------------------
// Sets up the alpha texture stage state
//-----------------------------------------------------------------------------
void CShaderShadowDX8::ConfigureAlphaPipe( unsigned int flags )
{
	// Are we using color?
	bool isUsingVertexAlpha = m_HasVertexAlpha && ((flags & SHADER_DRAW_COLOR) != 0);
	bool isUsingConstantAlpha = m_HasConstantAlpha;

	int lastTextureStage = m_pHardwareConfig->GetTextureStageCount() - 1;
	while ( lastTextureStage >= 0 )
	{
		if ( m_TextureStage[lastTextureStage].m_TextureAlphaEnable )
			break;
		--lastTextureStage;
	}

	for ( int i = 0; i < m_pHardwareConfig->GetTextureStageCount(); ++i )
	{
		m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_MODULATE;
		if ( m_TextureStage[i].m_TextureAlphaEnable )
		{
			if (i == 0)
			{
				m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE;
				m_ShadowState.m_TextureStage[i].m_AlphaArg2 = 
					isUsingConstantAlpha ? D3DTA_TFACTOR : D3DTA_DIFFUSE;
				if (!isUsingConstantAlpha && !isUsingVertexAlpha)
					m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_SELECTARG1;
				if (isUsingConstantAlpha)
					isUsingConstantAlpha = false;
				else if (isUsingVertexAlpha)
					isUsingVertexAlpha = false;
			}
			else
			{
				// Deal with texture stage 0
				m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE;
				m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_CURRENT;
			}
		}
		else
		{
			// Blat out unused stages
			if ((i > lastTextureStage) && !isUsingVertexAlpha && !isUsingConstantAlpha)
			{
				m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE;
				m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_CURRENT;
				m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_DISABLE;
				continue;
			}

			// No texture coordinates; try to fold in vertex or constant alpha
			if (i == 0)
			{
				m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TFACTOR;
				m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_DIFFUSE;
				if (isUsingVertexAlpha)
				{
					m_ShadowState.m_TextureStage[i].m_AlphaOp = 
						isUsingConstantAlpha ? D3DTOP_MODULATE : D3DTOP_SELECTARG2;
				}
				else
				{
					m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_SELECTARG1;
				}
				isUsingVertexAlpha = false;
				isUsingConstantAlpha = false; 
			}
			else
			{
				m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_CURRENT;
				if (isUsingConstantAlpha)
				{
					m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_TFACTOR;
					isUsingConstantAlpha = false;
				}
				else if (isUsingVertexAlpha)
				{
					m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_DIFFUSE;
					isUsingVertexAlpha = false;
				}
				else
				{
					m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_DIFFUSE;
					m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_SELECTARG1;
				}
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Sets up the texture stage state
//-----------------------------------------------------------------------------
void CShaderShadowDX8::ConfigureFVFVertexShader( unsigned int flags )
{
	// For non-modulation, we can't really use the path below...
	if (m_CustomTextureStageState)
	{
		ConfigureCustomFVFVertexShader( flags );
		return;
	}

	// Deal with texture stage 0
	m_ShadowState.m_TextureStage[0].m_ColorArg1 = D3DTA_TEXTURE;
	m_ShadowState.m_TextureStage[0].m_ColorArg2 = D3DTA_DIFFUSE;
	m_ShadowState.m_TextureStage[0].m_AlphaArg1 = D3DTA_TEXTURE;
	m_ShadowState.m_TextureStage[0].m_AlphaArg2 = D3DTA_DIFFUSE;

	// Are we using color?
	bool isUsingVertexColor = (flags & SHADER_DRAW_COLOR) != 0;
	bool isUsingConstantColor = (flags & SHADER_HAS_CONSTANT_COLOR) != 0;

	// Are we using texture coordinates?
	if ( IsUsingTextureCoordinates( SHADER_SAMPLER0 ) )
	{
		if (isUsingVertexColor)
		{
			m_ShadowState.m_TextureStage[0].m_ColorOp = OverbrightBlendValue(SHADER_TEXTURE_STAGE0);
			m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_MODULATE;
		}
		else
		{
			// Just blend in the constant color here, and don't blend it in below
			m_ShadowState.m_TextureStage[0].m_ColorArg2 = D3DTA_TFACTOR;
			m_ShadowState.m_TextureStage[0].m_AlphaArg2 = D3DTA_TFACTOR;
			isUsingConstantColor = false;

			m_ShadowState.m_TextureStage[0].m_ColorOp = OverbrightBlendValue(SHADER_TEXTURE_STAGE0);
			m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_MODULATE;
		}
	}
	else
	{
		// Are we using color?
		if (isUsingVertexColor)
		{
			// Color, but no texture
			if ( m_TextureStage[0].m_OverbrightVal < 2.0f )
			{
				// Use diffuse * constant color, if we have a constant color
				if (isUsingConstantColor)
				{
					m_ShadowState.m_TextureStage[0].m_ColorArg1 = D3DTA_TFACTOR;
					m_ShadowState.m_TextureStage[0].m_AlphaArg1 = D3DTA_TFACTOR;
					m_ShadowState.m_TextureStage[0].m_ColorOp = OverbrightBlendValue((TextureStage_t)0);
					m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_MODULATE;

					// This'll make sure we don't apply the constant color again below
					isUsingConstantColor = false;
				}
				else
				{
					m_ShadowState.m_TextureStage[0].m_ColorOp = D3DTOP_SELECTARG2;
					m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_SELECTARG2;
				}
			}
			else if (m_TextureStage[0].m_OverbrightVal < 4.0f)
			{
				// Produce diffuse + diffuse
				m_ShadowState.m_TextureStage[0].m_ColorArg1 = D3DTA_DIFFUSE;
				m_ShadowState.m_TextureStage[0].m_ColorOp = D3DTOP_ADD;
				m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_SELECTARG2;
			}
			else
			{
				// no 4x overbright yet!
				Assert(0);
			}
		}
		else
		{
			// No texture, no color
			if (isUsingConstantColor)
			{
				m_ShadowState.m_TextureStage[0].m_ColorArg1 = D3DTA_TFACTOR;
				m_ShadowState.m_TextureStage[0].m_AlphaArg1 = D3DTA_TFACTOR;
				m_ShadowState.m_TextureStage[0].m_ColorOp = D3DTOP_SELECTARG1;
				m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_SELECTARG1;

				// This'll make sure we don't apply the constant color again below
				isUsingConstantColor = false;
			}
			else
			{
				// Deal with texture stage 0
				m_ShadowState.m_TextureStage[0].m_ColorArg1 = D3DTA_TFACTOR;
				m_ShadowState.m_TextureStage[0].m_AlphaArg1 = D3DTA_TFACTOR;
				m_ShadowState.m_TextureStage[0].m_ColorOp = D3DTOP_SELECTARG1;
				m_ShadowState.m_TextureStage[0].m_AlphaOp = D3DTOP_SELECTARG1;
			}
		}
	}

	// Deal with texture stage 1 -> n
	int lastUsedTextureStage = 0;
	for ( int i = 1; i < m_pHardwareConfig->GetTextureStageCount(); ++i )
	{
		m_ShadowState.m_TextureStage[i].m_ColorArg1 = D3DTA_TEXTURE;
		m_ShadowState.m_TextureStage[i].m_ColorArg2 = D3DTA_CURRENT;
		m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE;
		m_ShadowState.m_TextureStage[i].m_AlphaArg2 = D3DTA_CURRENT;

		// Not doing anything? Disable the stage
		if ( !IsUsingTextureCoordinates( (Sampler_t)i ) )
		{
			if (m_TextureStage[i].m_OverbrightVal < 2.0f)
			{
				m_ShadowState.m_TextureStage[i].m_ColorOp = D3DTOP_DISABLE;
				m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_DISABLE;
			}
			else
			{
				// Here, we're modulating. Add in the constant color if we need to...
				m_ShadowState.m_TextureStage[i].m_ColorArg1 = D3DTA_TFACTOR;
				m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TFACTOR;

				m_ShadowState.m_TextureStage[i].m_ColorOp = OverbrightBlendValue((TextureStage_t)i);
				m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_MODULATE;

				isUsingConstantColor = false;
				lastUsedTextureStage = i;
			}
		}
		else							  
		{
			// Here, we're modulating. Keep track of the last modulation stage,
			// cause the constant color modulation comes in the stage after that
			lastUsedTextureStage = i;
			m_ShadowState.m_TextureStage[i].m_ColorOp = OverbrightBlendValue((TextureStage_t)i);
			m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_MODULATE;
		}
	}

	// massive amounts of suck: gotta overbright here if we really
	// wanted to overbright stage0 but couldn't because of the add.
	// This isn't totally correct, but there's no way around putting it here
	// because we can't texture out of stage2 on low or medium end hardware
	m_ShadowShaderState.m_ModulateConstantColor = false;
	if (isUsingConstantColor)
	{
		++lastUsedTextureStage;

		if (isUsingConstantColor &&
			(lastUsedTextureStage >= m_pHardwareConfig->GetTextureStageCount()))
		{
			// This is the case where we'd want to modulate in a particular texture
			// stage, but we can't because there aren't enough. In this case, we're gonna
			// need to do the modulation in the per-vertex color.
			m_ShadowShaderState.m_ModulateConstantColor = true;
		}
		else
		{
			AssertOnce (lastUsedTextureStage < 2);
				
			// Here, we've got enough texture stages to do the modulation
			m_ShadowState.m_TextureStage[lastUsedTextureStage].m_ColorArg1 = D3DTA_TFACTOR;
			m_ShadowState.m_TextureStage[lastUsedTextureStage].m_ColorArg2 = D3DTA_CURRENT;
			m_ShadowState.m_TextureStage[lastUsedTextureStage].m_AlphaArg1 = D3DTA_TFACTOR;
			m_ShadowState.m_TextureStage[lastUsedTextureStage].m_AlphaArg2 = D3DTA_CURRENT;
			m_ShadowState.m_TextureStage[lastUsedTextureStage].m_ColorOp = D3DTOP_MODULATE;
			m_ShadowState.m_TextureStage[lastUsedTextureStage].m_AlphaOp = D3DTOP_MODULATE;
		}
	}

	// Overwrite the alpha stuff if we asked to independently control it
	if (m_AlphaPipe)
	{
		ConfigureAlphaPipe( flags );
	}
}


//-----------------------------------------------------------------------------
// Makes sure we report if we're getting garbage.
//-----------------------------------------------------------------------------
void CShaderShadowDX8::DrawFlags( unsigned int flags )
{
	m_DrawFlags = flags;
	m_ShadowState.m_UsingFixedFunction = true;
}


//-----------------------------------------------------------------------------
// Compute texture coordinates
//-----------------------------------------------------------------------------
void CShaderShadowDX8::ConfigureTextureCoordinates( unsigned int flags )
{
	// default...
	for (int i = 0; i < m_pHardwareConfig->GetTextureStageCount(); ++i)
	{
		TextureCoordinate( (TextureStage_t)i, i );
	}

	if (flags & SHADER_DRAW_TEXCOORD0)
	{
		Assert( (flags & SHADER_DRAW_LIGHTMAP_TEXCOORD0) == 0 );
		TextureCoordinate( SHADER_TEXTURE_STAGE0, 0 );
	}
	else if (flags & SHADER_DRAW_LIGHTMAP_TEXCOORD0)
	{
		TextureCoordinate( SHADER_TEXTURE_STAGE0, 1 );
	}
	else if (flags & SHADER_DRAW_SECONDARY_TEXCOORD0 ) 
	{
		TextureCoordinate( SHADER_TEXTURE_STAGE0, 2 );
	}

	if (flags & SHADER_DRAW_TEXCOORD1)
	{
		Assert( (flags & SHADER_DRAW_LIGHTMAP_TEXCOORD1) == 0 );
		TextureCoordinate( SHADER_TEXTURE_STAGE1, 0 );
	}
	else if (flags & SHADER_DRAW_LIGHTMAP_TEXCOORD1)
	{
		TextureCoordinate( SHADER_TEXTURE_STAGE1, 1 );
	}
	else if (flags & SHADER_DRAW_SECONDARY_TEXCOORD1 ) 
	{
		TextureCoordinate( SHADER_TEXTURE_STAGE1, 2 );
	}
}


//-----------------------------------------------------------------------------
// Converts draw flags into vertex format
//-----------------------------------------------------------------------------
VertexFormat_t CShaderShadowDX8::FlagsToVertexFormat( int flags ) const
{
	// Flags -1 occurs when there's an error condition;
	// we'll just give em the max space and let them fill it in.
	int formatFlags = 0;
	int texCoordSize[VERTEX_MAX_TEXTURE_COORDINATES] = { 0, 0, 0, 0, 0, 0, 0, 0 };
	int userDataSize = 0;
	int numBones = 0;

	// Flags -1 occurs when there's an error condition;
	// we'll just give em the max space and let them fill it in.
	if (flags == -1)
	{
		formatFlags = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_COLOR |
			VERTEX_TANGENT_S | VERTEX_TANGENT_T;
		texCoordSize[0] = texCoordSize[1] = texCoordSize[2] = 2;
	}
	else
	{
		if (flags & SHADER_DRAW_POSITION)
			formatFlags |= VERTEX_POSITION;

		if (flags & SHADER_DRAW_NORMAL)
			formatFlags |= VERTEX_NORMAL;

		if (flags & SHADER_DRAW_COLOR)
			formatFlags |= VERTEX_COLOR;
		
		if( flags & SHADER_DRAW_SPECULAR )
			formatFlags |= VERTEX_SPECULAR;

		if (flags & SHADER_TEXCOORD_MASK)
		{
			// normal texture coords into texture 0
			texCoordSize[0] = 2;
		}

		if (flags & SHADER_LIGHTMAP_TEXCOORD_MASK)
		{
			// lightmaps go into texcoord 1
			texCoordSize[1] = 2;
		}

		if (flags & SHADER_SECONDARY_TEXCOORD_MASK)
		{
			// any texgen, or secondary texture coordinate is put into texcoord 2
			texCoordSize[2] = 2;
		}
	}

	// Hardware skinning...	always store space for up to 3 bones
	// and always assume index blend enabled if available
	if (m_ShadowState.m_VertexBlendEnable)
	{
		if (HardwareConfig()->MaxBlendMatrixIndices() > 0)
			formatFlags |= VERTEX_BONE_INDEX;

		if (HardwareConfig()->MaxBlendMatrices() > 2)
			numBones = 2;	// the third bone weight is implied
		else
			numBones = HardwareConfig()->MaxBlendMatrices() - 1;
	}

	return MeshMgr()->ComputeVertexFormat( formatFlags, VERTEX_MAX_TEXTURE_COORDINATES, 
		texCoordSize, numBones, userDataSize );
}


//-----------------------------------------------------------------------------
// Computes shadow state based on bunches of other parameters
//-----------------------------------------------------------------------------
void CShaderShadowDX8::ComputeAggregateShadowState( )
{
	unsigned int flags = 0;

	// Initialize the texture stage usage; this may get changed later
	for (int i = 0; i < m_pHardwareConfig->GetSamplerCount(); ++i)
	{
		m_ShadowState.m_SamplerState[i].m_TextureEnable = 
			IsUsingTextureCoordinates( (Sampler_t)i );

		// Deal with the alpha pipe
		if ( m_ShadowState.m_UsingFixedFunction && m_AlphaPipe )
		{
			if ( m_TextureStage[i].m_TextureAlphaEnable )
			{
				m_ShadowState.m_SamplerState[i].m_TextureEnable = true;
			}
		}
	}

	// Always use the same alpha src + dest if it's disabled
	// NOTE: This is essential for stateblocks to work
	if ( m_ShadowState.m_AlphaBlendEnable )
	{
		m_ShadowState.m_SrcBlend = m_SrcBlend;
		m_ShadowState.m_DestBlend = m_DestBlend;
		m_ShadowState.m_BlendOp = m_BlendOp;
	}
	else
	{
		m_ShadowState.m_SrcBlend = D3DBLEND_ONE;
		m_ShadowState.m_DestBlend = D3DBLEND_ZERO;
		m_ShadowState.m_BlendOp = D3DBLENDOP_ADD;
	}

	// GR
	if (m_ShadowState.m_SeparateAlphaBlendEnable)
	{
		m_ShadowState.m_SrcBlendAlpha = m_SrcBlendAlpha;
		m_ShadowState.m_DestBlendAlpha = m_DestBlendAlpha;
		m_ShadowState.m_BlendOpAlpha = m_BlendOpAlpha;
	}
	else
	{
		m_ShadowState.m_SrcBlendAlpha = D3DBLEND_ONE;
		m_ShadowState.m_DestBlendAlpha = D3DBLEND_ZERO;
		m_ShadowState.m_BlendOpAlpha = D3DBLENDOP_ADD;
	}

	// Use the same func if it's disabled
	if (m_ShadowState.m_AlphaTestEnable)
	{
		// If alpha test is enabled, just use the values set
		m_ShadowState.m_AlphaFunc = m_AlphaFunc;
		m_ShadowState.m_AlphaRef = m_AlphaRef;
	}
	else
	{
		// A default value
		m_ShadowState.m_AlphaFunc = D3DCMP_GREATEREQUAL;
		m_ShadowState.m_AlphaRef = 0;

		// If not alpha testing and doing a standard alpha blend, force on alpha testing
		if ( m_ShadowState.m_AlphaBlendEnable )
		{
			if ( ( m_ShadowState.m_SrcBlend == D3DBLEND_SRCALPHA ) && ( m_ShadowState.m_DestBlend == D3DBLEND_INVSRCALPHA ) )
			{
				m_ShadowState.m_AlphaFunc = D3DCMP_GREATEREQUAL;
				m_ShadowState.m_AlphaRef = 1;
			}
		}
	}
	if ( m_ShadowState.m_UsingFixedFunction )
	{
		flags = m_DrawFlags;

		// We need to take this bad boy into account
		if (HasConstantColor())
			flags |= SHADER_HAS_CONSTANT_COLOR;

		// We need to take lighting into account..
		if ( m_ShadowState.m_Lighting )
			flags |= SHADER_DRAW_NORMAL;

		if (m_ShadowState.m_Lighting)
			flags |= SHADER_DRAW_COLOR;

		// Look for inconsistency in the shadow state (can't have texgen &
		// SHADER_DRAW_TEXCOORD or SHADER_DRAW_SECONDARY_TEXCOORD0 on the same stage)
		if (flags & (SHADER_DRAW_TEXCOORD0 | SHADER_DRAW_SECONDARY_TEXCOORD0))
		{
			Assert( (m_ShadowState.m_TextureStage[0].m_TexCoordIndex & 0xFFFF0000) == 0 );
		}
		if (flags & (SHADER_DRAW_TEXCOORD1 | SHADER_DRAW_SECONDARY_TEXCOORD1))
		{
			Assert( (m_ShadowState.m_TextureStage[1].m_TexCoordIndex & 0xFFFF0000) == 0 );
		}			
		if (flags & (SHADER_DRAW_TEXCOORD2 | SHADER_DRAW_SECONDARY_TEXCOORD2))
		{
			Assert( (m_ShadowState.m_TextureStage[2].m_TexCoordIndex & 0xFFFF0000) == 0 );
		}
		if (flags & (SHADER_DRAW_TEXCOORD3 | SHADER_DRAW_SECONDARY_TEXCOORD3))
		{
			Assert( (m_ShadowState.m_TextureStage[3].m_TexCoordIndex & 0xFFFF0000) == 0 );
		}

		// Vertex usage has already been set for pixel + vertex shaders
		m_ShadowShaderState.m_VertexUsage = FlagsToVertexFormat( flags );

		// Configure the texture stages
		ConfigureFVFVertexShader(flags);

#if 0
//#ifdef _DEBUG
		// NOTE: This must be true for stateblocks to work
		for ( i = 0; i < m_pHardwareConfig->GetTextureStageCount(); ++i )
		{
			if ( m_ShadowState.m_TextureStage[i].m_ColorOp == D3DTOP_DISABLE )
			{
				Assert( m_ShadowState.m_TextureStage[i].m_ColorArg1 == D3DTA_TEXTURE );
 				Assert( m_ShadowState.m_TextureStage[i].m_ColorArg2 == ((i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT) );
			}

			if ( m_ShadowState.m_TextureStage[i].m_AlphaOp == D3DTOP_DISABLE )
			{
				Assert( m_ShadowState.m_TextureStage[i].m_AlphaArg1 == D3DTA_TEXTURE );
 				Assert( m_ShadowState.m_TextureStage[i].m_AlphaArg2 == ((i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT) );
			}
		}
#endif
	}
	else
	{
		// Pixel shaders, disable everything so as to prevent unnecessary state changes....
		for ( int i = 0; i < m_pHardwareConfig->GetTextureStageCount(); ++i )
		{
			m_ShadowState.m_TextureStage[i].m_ColorArg1 = D3DTA_TEXTURE;
			m_ShadowState.m_TextureStage[i].m_ColorArg2 = (i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT;
			m_ShadowState.m_TextureStage[i].m_AlphaArg1 = D3DTA_TEXTURE;
			m_ShadowState.m_TextureStage[i].m_AlphaArg2 = (i == 0) ? D3DTA_DIFFUSE : D3DTA_CURRENT;
 			m_ShadowState.m_TextureStage[i].m_ColorOp = D3DTOP_DISABLE;
			m_ShadowState.m_TextureStage[i].m_AlphaOp = D3DTOP_DISABLE;
			m_ShadowState.m_TextureStage[i].m_TexCoordIndex = i;
		}
		m_ShadowState.m_Lighting = false;
		m_ShadowState.m_SpecularEnable = false;
		m_ShadowState.m_VertexBlendEnable = false;
		m_ShadowShaderState.m_ModulateConstantColor = false;
	}

	// Compute texture coordinates
	ConfigureTextureCoordinates(flags);

	// Alpha to coverage
	if ( m_ShadowState.m_EnableAlphaToCoverage )
	{
		// Only allow this to be enabled if blending is disabled and testing is enabled
		if ( ( m_ShadowState.m_AlphaBlendEnable == true ) || ( m_ShadowState.m_AlphaTestEnable == false ) )
		{
			m_ShadowState.m_EnableAlphaToCoverage = false;
		}
	}
}

void CShaderShadowDX8::FogMode( ShaderFogMode_t fogMode )
{
	Assert( fogMode >= 0 && fogMode < SHADER_FOGMODE_NUMFOGMODES );
	m_ShadowState.m_FogMode = fogMode;
}

void CShaderShadowDX8::DisableFogGammaCorrection( bool bDisable )
{
	m_ShadowState.m_bDisableFogGammaCorrection = bDisable;
}

void CShaderShadowDX8::SetDiffuseMaterialSource( ShaderMaterialSource_t materialSource )
{
	COMPILE_TIME_ASSERT( ( int )D3DMCS_MATERIAL == ( int )SHADER_MATERIALSOURCE_MATERIAL );
	COMPILE_TIME_ASSERT( ( int )D3DMCS_COLOR1 == ( int )SHADER_MATERIALSOURCE_COLOR1 );
	COMPILE_TIME_ASSERT( ( int )D3DMCS_COLOR2 == ( int )SHADER_MATERIALSOURCE_COLOR2 );
	Assert( materialSource == SHADER_MATERIALSOURCE_MATERIAL ||
			materialSource == SHADER_MATERIALSOURCE_COLOR1 ||
			materialSource == SHADER_MATERIALSOURCE_COLOR2 );
	m_ShadowState.m_DiffuseMaterialSource = ( D3DMATERIALCOLORSOURCE )materialSource;
}