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

#include "BaseVSShader.h"
#include <string.h>
#include "const.h"

#include "cpp_shader_constant_register_map.h"

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

#include "sprite_vs20.inc"
#include "sprite_ps20.inc"
#include "sprite_ps20b.inc"

// WARNING!  Change these in engine/SpriteGn.h if you change them here!
#define SPR_VP_PARALLEL_UPRIGHT		0
#define SPR_FACING_UPRIGHT			1
#define SPR_VP_PARALLEL				2
#define SPR_ORIENTED				3
#define SPR_VP_PARALLEL_ORIENTED	4


DEFINE_FALLBACK_SHADER( Sprite, Sprite_DX9 )

BEGIN_VS_SHADER( Sprite_DX9, 
			  "Help for Sprite_DX9" )
			  
	BEGIN_SHADER_PARAMS
		SHADER_PARAM( SPRITEORIGIN, SHADER_PARAM_TYPE_VEC3, "[0 0 0]", "sprite origin" )
		SHADER_PARAM( SPRITEORIENTATION, SHADER_PARAM_TYPE_INTEGER, "0", "sprite orientation" )
		SHADER_PARAM( SPRITERENDERMODE, SHADER_PARAM_TYPE_INTEGER, "0", "sprite rendermode" )
		SHADER_PARAM( IGNOREVERTEXCOLORS, SHADER_PARAM_TYPE_BOOL, "1", "ignore vertex colors" )
		SHADER_PARAM( NOSRGB, SHADER_PARAM_TYPE_BOOL, "0", "do not operate in srgb space" )
		SHADER_PARAM( HDRCOLORSCALE, SHADER_PARAM_TYPE_FLOAT, "1.0", "hdr color scale" )
	END_SHADER_PARAMS

	SHADER_FALLBACK
	{
		if (g_pHardwareConfig->GetDXSupportLevel() < 90)
			return "Sprite_DX8";
		return 0;
	}
	SHADER_INIT_PARAMS()
	{
		// FIXME: This can share code with sprite.cpp
		if (!params[ALPHA]->IsDefined())
		{
			params[ALPHA]->SetFloatValue( 1.0f );
		}

		if (!params[HDRCOLORSCALE]->IsDefined())
		{
			params[HDRCOLORSCALE]->SetFloatValue( 1.0f );
		}

		if ( !params[NOSRGB]->IsDefined() )
		{
			// Disable sRGB reads and writes by default
			params[NOSRGB]->SetIntValue( 1 );
		}

		SET_FLAGS( MATERIAL_VAR_NO_DEBUG_OVERRIDE );
		SET_FLAGS( MATERIAL_VAR_VERTEXCOLOR );
		SET_FLAGS( MATERIAL_VAR_VERTEXALPHA );

		// translate from a string orientation to an enumeration
		if (params[SPRITEORIENTATION]->IsDefined())
		{
			const char *orientationString = params[SPRITEORIENTATION]->GetStringValue();
			if( stricmp( orientationString, "parallel_upright" ) == 0 )
			{
				params[SPRITEORIENTATION]->SetIntValue( SPR_VP_PARALLEL_UPRIGHT );
			}
			else if( stricmp( orientationString, "facing_upright" ) == 0 )
			{
				params[SPRITEORIENTATION]->SetIntValue( SPR_FACING_UPRIGHT );
			}
			else if( stricmp( orientationString, "vp_parallel" ) == 0 )
			{
				params[SPRITEORIENTATION]->SetIntValue( SPR_VP_PARALLEL );
			}
			else if( stricmp( orientationString, "oriented" ) == 0 )
			{
				params[SPRITEORIENTATION]->SetIntValue( SPR_ORIENTED );
			}
			else if( stricmp( orientationString, "vp_parallel_oriented" ) == 0 )
			{
				params[SPRITEORIENTATION]->SetIntValue( SPR_VP_PARALLEL_ORIENTED );
			}
			else
			{
				Warning( "error with $spriteOrientation\n" );
				params[SPRITEORIENTATION]->SetIntValue( SPR_VP_PARALLEL_UPRIGHT );
			}
		}
		else
		{
			// default case
			params[SPRITEORIENTATION]->SetIntValue( SPR_VP_PARALLEL_UPRIGHT );
		}
	}

	SHADER_INIT
	{
		bool bSRGB = s_ppParams[NOSRGB]->GetIntValue() == 0;
		LoadTexture( BASETEXTURE, bSRGB ? TEXTUREFLAGS_SRGB : 0 );
	}

#define SHADER_USE_VERTEX_COLOR		1
#define SHADER_USE_CONSTANT_COLOR	2

	void SetSpriteCommonShadowState( unsigned int shaderFlags )
	{
		IShaderShadow *pShaderShadow = s_pShaderShadow;
		s_pShaderShadow->EnableTexture( SHADER_SAMPLER0, true );
		bool bSRGB = s_ppParams[NOSRGB]->GetIntValue() == 0;
		pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, bSRGB );

		// Only enabling this on OSX() - it causes GL mode's light glow sprites to be much darker vs. D3D9 under Linux/Win GL.
		bool bSRGBOutputAdapter = ( IsOSX() && !g_pHardwareConfig->FakeSRGBWrite() ) && !bSRGB;

		unsigned int flags = VERTEX_POSITION;
		if( shaderFlags & SHADER_USE_VERTEX_COLOR )
		{
			flags |= VERTEX_COLOR;
		}
		int numTexCoords = 1;
		s_pShaderShadow->VertexShaderVertexFormat( flags, numTexCoords, 0, 0 );

		DECLARE_STATIC_VERTEX_SHADER( sprite_vs20 );
		SET_STATIC_VERTEX_SHADER_COMBO( VERTEXCOLOR,  ( shaderFlags & SHADER_USE_VERTEX_COLOR ) ? true : false );
		SET_STATIC_VERTEX_SHADER_COMBO( SRGB,  bSRGB );
		SET_STATIC_VERTEX_SHADER( sprite_vs20 );

		if( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL down this path
		{
			DECLARE_STATIC_PIXEL_SHADER( sprite_ps20b );
			SET_STATIC_PIXEL_SHADER_COMBO( VERTEXCOLOR,  ( shaderFlags &  SHADER_USE_VERTEX_COLOR ) ? true : false );
			SET_STATIC_PIXEL_SHADER_COMBO( CONSTANTCOLOR,  ( shaderFlags & SHADER_USE_CONSTANT_COLOR ) ? true : false );
			SET_STATIC_PIXEL_SHADER_COMBO( HDRTYPE,  g_pHardwareConfig->GetHDRType() );
			SET_STATIC_PIXEL_SHADER_COMBO( SRGB, bSRGB );
			SET_STATIC_PIXEL_SHADER_COMBO( SRGB_OUTPUT_ADAPTER, bSRGBOutputAdapter );
			SET_STATIC_PIXEL_SHADER( sprite_ps20b );
		}
		else
		{
			DECLARE_STATIC_PIXEL_SHADER( sprite_ps20 );
			SET_STATIC_PIXEL_SHADER_COMBO( VERTEXCOLOR,  ( shaderFlags &  SHADER_USE_VERTEX_COLOR ) ? true : false );
			SET_STATIC_PIXEL_SHADER_COMBO( CONSTANTCOLOR,  ( shaderFlags & SHADER_USE_CONSTANT_COLOR ) ? true : false );
			SET_STATIC_PIXEL_SHADER_COMBO( HDRTYPE,  g_pHardwareConfig->GetHDRType() );
			SET_STATIC_PIXEL_SHADER_COMBO( SRGB, bSRGB );
			SET_STATIC_PIXEL_SHADER( sprite_ps20 );
		}

		// OSX always has to sRGB write (don't do this on Linux/Win GL - it causes glow sprites to be way too dark)
		s_pShaderShadow->EnableSRGBWrite( bSRGB || ( IsOSX() && !g_pHardwareConfig->FakeSRGBWrite() ) );
	}

	void SetSpriteCommonDynamicState( unsigned int shaderFlags )
	{
		IShaderDynamicAPI *pShaderAPI = s_pShaderAPI;
		bool bSRGB = s_ppParams[NOSRGB]->GetIntValue() == 0;

		BindTexture( SHADER_SAMPLER0, BASETEXTURE, FRAME );

		MaterialFogMode_t fogType = s_pShaderAPI->GetSceneFogMode();
		int fogIndex = ( fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ) ? 1 : 0;
		DECLARE_DYNAMIC_VERTEX_SHADER( sprite_vs20 );
		SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG,  fogIndex );
		SET_DYNAMIC_VERTEX_SHADER( sprite_vs20 );

		if( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL down this path
		{
			DECLARE_DYNAMIC_PIXEL_SHADER( sprite_ps20b );
			SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED, IsHDREnabled() );
			SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() );
			SET_DYNAMIC_PIXEL_SHADER( sprite_ps20b );
		}
		else
		{
			DECLARE_DYNAMIC_PIXEL_SHADER( sprite_ps20 );
			SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED, IsHDREnabled() );
			SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() );
			SET_DYNAMIC_PIXEL_SHADER( sprite_ps20 );
		}

		pShaderAPI->SetPixelShaderFogParams( PSREG_FOG_PARAMS );

		float vEyePos_SpecExponent[4];
		pShaderAPI->GetWorldSpaceCameraPosition( vEyePos_SpecExponent );
		vEyePos_SpecExponent[3] = 0.0f;
		pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vEyePos_SpecExponent, 1 );

		if( shaderFlags & SHADER_USE_CONSTANT_COLOR )
		{
			if ( bSRGB )
				SetPixelShaderConstantGammaToLinear( 0, COLOR, ALPHA );
			else
				SetPixelShaderConstant( 0, COLOR, ALPHA );
		}

		if( IsHDREnabled() )
		{
			if ( bSRGB )
				SetPixelShaderConstantGammaToLinear( 1, HDRCOLORSCALE );
			else
				SetPixelShaderConstant( 1, HDRCOLORSCALE );
		}
	}

	SHADER_DRAW
	{
		bool bSRGB = params[NOSRGB]->GetIntValue() == 0;

		SHADOW_STATE
		{
			pShaderShadow->EnableCulling( false );
		}

		switch( params[SPRITERENDERMODE]->GetIntValue() )
		{
		case kRenderNormal:
			SHADOW_STATE
			{
				FogToFogColor();

				SetSpriteCommonShadowState( 0 );
			}
			DYNAMIC_STATE
			{
				SetSpriteCommonDynamicState( 0 );
			}
			Draw();
			break;
		case kRenderTransColor:
		case kRenderTransTexture:
			SHADOW_STATE
			{
				pShaderShadow->EnableDepthWrites( false );
				pShaderShadow->EnableBlending( true );
				pShaderShadow->BlendFunc( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE_MINUS_SRC_ALPHA );
				
				FogToFogColor();

				SetSpriteCommonShadowState( SHADER_USE_VERTEX_COLOR );
			}
			DYNAMIC_STATE
			{
				SetSpriteCommonDynamicState( SHADER_USE_VERTEX_COLOR );
			}
			Draw();
			break;
		case kRenderGlow:
		case kRenderWorldGlow:
			SHADOW_STATE
			{
				pShaderShadow->EnableDepthWrites( false );
				pShaderShadow->EnableDepthTest( false );
				pShaderShadow->EnableBlending( true );
				pShaderShadow->BlendFunc( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE );
				
				FogToBlack();

				SetSpriteCommonShadowState( SHADER_USE_VERTEX_COLOR );
			}
			DYNAMIC_STATE
			{
				SetSpriteCommonDynamicState( SHADER_USE_VERTEX_COLOR );
			}
			Draw();
			break;
		case kRenderTransAlpha:
			// untested cut and past from kRenderTransAlphaAdd  . . same as first pass of that.
			SHADOW_STATE
			{
				pShaderShadow->EnableDepthWrites( false );
				pShaderShadow->EnableBlending( true );
				pShaderShadow->BlendFunc( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE_MINUS_SRC_ALPHA );
				
				FogToFogColor();

				SetSpriteCommonShadowState( SHADER_USE_VERTEX_COLOR );
			}
			DYNAMIC_STATE
			{
				SetSpriteCommonDynamicState( SHADER_USE_VERTEX_COLOR );
			}
			Draw();
			break;
		case kRenderTransAlphaAdd:
			SHADOW_STATE
			{
				pShaderShadow->EnableDepthWrites( false );
				pShaderShadow->EnableBlending( true );
				pShaderShadow->BlendFunc( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE_MINUS_SRC_ALPHA );
				
				FogToFogColor();

				SetSpriteCommonShadowState( SHADER_USE_VERTEX_COLOR );
			}
			DYNAMIC_STATE
			{
				SetSpriteCommonDynamicState( SHADER_USE_VERTEX_COLOR );
			}
			Draw();

			SHADOW_STATE
			{
				SetInitialShadowState();
				pShaderShadow->EnableDepthWrites( false );
				pShaderShadow->EnableBlending( true );
				pShaderShadow->BlendFunc( SHADER_BLEND_ONE_MINUS_SRC_ALPHA, SHADER_BLEND_ONE );
				
				FogToBlack();

				SetSpriteCommonShadowState( SHADER_USE_VERTEX_COLOR );
			}
			DYNAMIC_STATE
			{
				SetSpriteCommonDynamicState( SHADER_USE_VERTEX_COLOR );
			}
			Draw();
			break;

		case kRenderTransAdd:
			{
				unsigned int flags = SHADER_USE_CONSTANT_COLOR;
				if( !params[ IGNOREVERTEXCOLORS ]->GetIntValue() )
				{
					flags |= SHADER_USE_VERTEX_COLOR;
				}
				SHADOW_STATE
				{
					pShaderShadow->EnableDepthWrites( false );
					pShaderShadow->EnableBlending( true );
					pShaderShadow->BlendFunc( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE );
					
					FogToBlack();

					SetSpriteCommonShadowState( flags );
				}
				DYNAMIC_STATE
				{
					SetSpriteCommonDynamicState( flags );
				}
			}
			Draw();
			break;
		case kRenderTransAddFrameBlend:
			{
				float flFrame = params[FRAME]->GetFloatValue();
				float flFade = params[ALPHA]->GetFloatValue();
				unsigned int flags = SHADER_USE_CONSTANT_COLOR;
				if( !params[ IGNOREVERTEXCOLORS ]->GetIntValue() )
				{
					flags |= SHADER_USE_VERTEX_COLOR;
				}
				SHADOW_STATE
				{
					pShaderShadow->EnableDepthWrites( false );
					pShaderShadow->EnableBlending( true );
					pShaderShadow->BlendFunc( SHADER_BLEND_SRC_ALPHA, SHADER_BLEND_ONE );
					
					FogToBlack();

					SetSpriteCommonShadowState( flags );
				}
				DYNAMIC_STATE
				{
					float frameBlendAlpha = 1.0f - ( flFrame - ( int )flFrame );
					ITexture *pTexture = params[BASETEXTURE]->GetTextureValue();
					BindTexture( SHADER_SAMPLER0, pTexture, ( int )flFrame );

					MaterialFogMode_t fogType = s_pShaderAPI->GetSceneFogMode();
					int fogIndex = ( fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ) ? 1 : 0;
					DECLARE_DYNAMIC_VERTEX_SHADER( sprite_vs20 );
					SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG,  fogIndex );
					SET_DYNAMIC_VERTEX_SHADER( sprite_vs20 );

					if( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL down this path
					{
						DECLARE_DYNAMIC_PIXEL_SHADER( sprite_ps20b );
						SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED,  IsHDREnabled() );
						SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() );
						SET_DYNAMIC_PIXEL_SHADER( sprite_ps20b );
					}
					else
					{
						DECLARE_DYNAMIC_PIXEL_SHADER( sprite_ps20 );
						SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED,  IsHDREnabled() );
						SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() );
						SET_DYNAMIC_PIXEL_SHADER( sprite_ps20 );
					}

					pShaderAPI->SetPixelShaderFogParams( PSREG_FOG_PARAMS );

					float vEyePos_SpecExponent[4];
					pShaderAPI->GetWorldSpaceCameraPosition( vEyePos_SpecExponent );
					vEyePos_SpecExponent[3] = 0.0f;
					pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vEyePos_SpecExponent, 1 );

					float color[4];
					if ( bSRGB )
						color[0] = color[1] = color[2] = GammaToLinear( flFade * frameBlendAlpha );
					else
						color[0] = color[1] = color[2] = flFade * frameBlendAlpha;
					color[3] = 1.0f;
					s_pShaderAPI->SetPixelShaderConstant( 0, color );
					if( IsHDREnabled() )
					{
						if ( bSRGB )
							SetPixelShaderConstantGammaToLinear( 1, HDRCOLORSCALE );
						else
							SetPixelShaderConstant( 1, HDRCOLORSCALE );
					}
				}
				Draw();
				SHADOW_STATE
				{
					FogToBlack();

					SetSpriteCommonShadowState( flags );
				}
				DYNAMIC_STATE
				{
					float frameBlendAlpha = ( flFrame - ( int )flFrame );
					ITexture *pTexture = params[BASETEXTURE]->GetTextureValue();
					int numAnimationFrames = pTexture->GetNumAnimationFrames();
					BindTexture( SHADER_SAMPLER0, pTexture, ( ( int )flFrame + 1 ) % numAnimationFrames );

					MaterialFogMode_t fogType = s_pShaderAPI->GetSceneFogMode();
					int fogIndex = ( fogType == MATERIAL_FOG_LINEAR_BELOW_FOG_Z ) ? 1 : 0;
					DECLARE_DYNAMIC_VERTEX_SHADER( sprite_vs20 );
					SET_DYNAMIC_VERTEX_SHADER_COMBO( DOWATERFOG,  fogIndex );
					SET_DYNAMIC_VERTEX_SHADER( sprite_vs20 );

					if( g_pHardwareConfig->SupportsPixelShaders_2_b() || g_pHardwareConfig->ShouldAlwaysUseShaderModel2bShaders() ) // Always send GL down this path
					{
						DECLARE_DYNAMIC_PIXEL_SHADER( sprite_ps20b );
						SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED,  IsHDREnabled() );
						SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() );
						SET_DYNAMIC_PIXEL_SHADER( sprite_ps20b );
					}
					else
					{
						DECLARE_DYNAMIC_PIXEL_SHADER( sprite_ps20 );
						SET_DYNAMIC_PIXEL_SHADER_COMBO( HDRENABLED,  IsHDREnabled() );
						SET_DYNAMIC_PIXEL_SHADER_COMBO( PIXELFOGTYPE, pShaderAPI->GetPixelFogCombo() );
						SET_DYNAMIC_PIXEL_SHADER( sprite_ps20 );
					}

					pShaderAPI->SetPixelShaderFogParams( PSREG_FOG_PARAMS );

					float vEyePos_SpecExponent[4];
					pShaderAPI->GetWorldSpaceCameraPosition( vEyePos_SpecExponent );
					vEyePos_SpecExponent[3] = 0.0f;
					pShaderAPI->SetPixelShaderConstant( PSREG_EYEPOS_SPEC_EXPONENT, vEyePos_SpecExponent, 1 );

					float color[4];
					if ( bSRGB )
						color[0] = color[1] = color[2] = GammaToLinear( flFade * frameBlendAlpha );
					else
						color[0] = color[1] = color[2] = flFade * frameBlendAlpha;
					color[3] = 1.0f;
					s_pShaderAPI->SetPixelShaderConstant( 0, color );
					if( IsHDREnabled() )
					{
						if ( bSRGB )
							SetPixelShaderConstantGammaToLinear( 1, HDRCOLORSCALE );
						else
							SetPixelShaderConstant( 1, HDRCOLORSCALE );
					}
				}
				Draw();
			}

			break;
		default:
			ShaderWarning( "shader Sprite: Unknown sprite render mode\n" );
			break;
		}
	}
END_SHADER