//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose: 
//
// $NoKeywords: $
// This is what all vs/ps (dx8+) shaders inherit from.
//===========================================================================//

#ifndef BASEVSSHADER_H
#define BASEVSSHADER_H

#ifdef _WIN32		   
#pragma once
#endif

#include "cpp_shader_constant_register_map.h"
#include "shaderlib/cshader.h"
#include "shaderlib/BaseShader.h"
#include "shaderapifast.h"
#include "convar.h"
#include <renderparm.h>


// Texture combining modes for combining base and detail/basetexture2
// Matches what's in common_ps_fxc.h
#define DETAIL_BLEND_MODE_RGB_EQUALS_BASE_x_DETAILx2				0	// Original mode (Mod2x)
#define DETAIL_BLEND_MODE_RGB_ADDITIVE								1	// Base.rgb+detail.rgb*fblend
#define DETAIL_BLEND_MODE_DETAIL_OVER_BASE							2
#define DETAIL_BLEND_MODE_FADE										3	// Straight fade between base and detail.
#define DETAIL_BLEND_MODE_BASE_OVER_DETAIL							4	// Use base alpha for blend over detail
#define DETAIL_BLEND_MODE_RGB_ADDITIVE_SELFILLUM					5	// Add detail color post lighting
#define DETAIL_BLEND_MODE_RGB_ADDITIVE_SELFILLUM_THRESHOLD_FADE		6
#define DETAIL_BLEND_MODE_MOD2X_SELECT_TWO_PATTERNS					7	// Use alpha channel of base to select between mod2x channels in r+a of detail
#define DETAIL_BLEND_MODE_MULTIPLY									8
#define DETAIL_BLEND_MODE_MASK_BASE_BY_DETAIL_ALPHA					9	// Use alpha channel of detail to mask base
#define DETAIL_BLEND_MODE_SSBUMP_BUMP								10	// Use detail to modulate lighting as an ssbump
#define DETAIL_BLEND_MODE_SSBUMP_NOBUMP								11	// Detail is an ssbump but use it as an albedo. shader does the magic here - no user needs to specify mode 11
#define DETAIL_BLEND_MODE_NONE										12	// There is no detail texture

// Texture combining modes for combining base and decal texture
#define DECAL_BLEND_MODE_DECAL_ALPHA								0	// Original mode ( = decalRGB*decalA + baseRGB*(1-decalA))
#define DECAL_BLEND_MODE_RGB_MOD1X									1	// baseRGB * decalRGB
#define DECAL_BLEND_MODE_NONE										2	// There is no decal texture

// We force aniso on certain textures for the consoles only
#if defined( _GAMECONSOLE )
	#define ANISOTROPIC_OVERRIDE TEXTUREFLAGS_ANISOTROPIC
#else
	#define ANISOTROPIC_OVERRIDE 0
#endif

//-----------------------------------------------------------------------------
// Helper macro for vertex shaders
//-----------------------------------------------------------------------------
#define BEGIN_VS_SHADER_FLAGS(_name, _help, _flags)	__BEGIN_SHADER_INTERNAL( CBaseVSShader, _name, _help, _flags )
#define BEGIN_VS_SHADER(_name,_help)	__BEGIN_SHADER_INTERNAL( CBaseVSShader, _name, _help, 0 )


// useful parameter initialization macro
#define INIT_FLOAT_PARM( parm, value )					\
		if ( !params[(parm)]->IsDefined() )				\
		{												\
			params[(parm)]->SetFloatValue( (value) );	\
		}

// useful pixel shader declaration macro for ps20/20b c++ code
#define SET_STATIC_PS2X_PIXEL_SHADER_NO_COMBOS( basename )		\
		if( g_pHardwareConfig->SupportsPixelShaders_2_b() )		\
		{														\
			DECLARE_STATIC_PIXEL_SHADER( basename##_ps20b );	\
			SET_STATIC_PIXEL_SHADER( basename##_ps20b );		\
		}														\
		else													\
		{														\
			DECLARE_STATIC_PIXEL_SHADER( basename##_ps20 );		\
			SET_STATIC_PIXEL_SHADER( basename##_ps20 );			\
		}

#define SET_DYNAMIC_PS2X_PIXEL_SHADER_NO_COMBOS( basename )		\
		if( g_pHardwareConfig->SupportsPixelShaders_2_b() )		\
		{														\
			DECLARE_DYNAMIC_PIXEL_SHADER( basename##_ps20b );	\
			SET_DYNAMIC_PIXEL_SHADER( basename##_ps20b );		\
		}														\
		else													\
		{														\
			DECLARE_DYNAMIC_PIXEL_SHADER( basename##_ps20 );		\
			SET_DYNAMIC_PIXEL_SHADER( basename##_ps20 );			\
		}


//-----------------------------------------------------------------------------
// Base class for shaders, contains helper methods.
//-----------------------------------------------------------------------------
class CBaseVSShader : public CBaseShader
{
public:
	// Loads bump lightmap coordinates into the pixel shader
	void LoadBumpLightmapCoordinateAxes_PixelShader( int pixelReg );

	// Loads bump lightmap coordinates into the vertex shader
	void LoadBumpLightmapCoordinateAxes_VertexShader( int vertexReg );

	// Pixel and vertex shader constants....
	void SetPixelShaderConstant( int pixelReg, int constantVar );

	// Pixel and vertex shader constants....
	void SetPixelShaderConstantGammaToLinear( int pixelReg, int constantVar );

	// This version will put constantVar into x,y,z, and constantVar2 into the w
	void SetPixelShaderConstant( int pixelReg, int constantVar, int constantVar2 );
	void SetPixelShaderConstantGammaToLinear( int pixelReg, int constantVar, int constantVar2 );

	// Helpers for setting constants that need to be converted to linear space (from gamma space).
	void SetVertexShaderConstantGammaToLinear( int var, float const* pVec, int numConst = 1, bool bForce = false );
	void SetPixelShaderConstantGammaToLinear( int var, float const* pVec, int numConst = 1, bool bForce = false );

	void SetVertexShaderConstant( int vertexReg, int constantVar );

	// set rgb components of constant from a color parm and give an explicit w value
	void SetPixelShaderConstant_W( int pixelReg, int constantVar, float fWValue );

	// GR - fix for const/lerp issues
	void SetPixelShaderConstantFudge( int pixelReg, int constantVar );

	// Sets vertex shader texture transforms
	void SetVertexShaderTextureTranslation( int vertexReg, int translationVar );
	void SetVertexShaderTextureScale( int vertexReg, int scaleVar );
 	void SetVertexShaderTextureTransform( int vertexReg, int transformVar );
	void SetVertexShaderTextureScaledTransform( int vertexReg, 
											int transformVar, int scaleVar );

	// Set pixel shader texture transforms
	void SetPixelShaderTextureTranslation( int pixelReg, int translationVar );
	void SetPixelShaderTextureScale( int pixelReg, int scaleVar );
 	void SetPixelShaderTextureTransform( int pixelReg, int transformVar );
	void SetPixelShaderTextureScaledTransform( int pixelReg, 
											int transformVar, int scaleVar );

	// Moves a matrix into vertex shader constants 
	void SetVertexShaderMatrix3x4( int vertexReg, int matrixVar );
	void SetVertexShaderMatrix4x4( int vertexReg, int matrixVar );

	// Loads the view matrix into vertex shader constants
	void LoadViewMatrixIntoVertexShaderConstant( int vertexReg );

	// Loads the projection matrix into vertex shader constants
	void LoadProjectionMatrixIntoVertexShaderConstant( int vertexReg );

	// Loads the model->view matrix into vertex shader constants
	void LoadModelViewMatrixIntoVertexShaderConstant( int vertexReg );

	// Helpers for dealing with envmaptint
	void SetEnvMapTintPixelShaderDynamicState( int pixelReg, int tintVar, int alphaVar, bool bConvertFromGammaToLinear = false );
	
	// Helper methods for pixel shader overbrighting
	void EnablePixelShaderOverbright( int reg, bool bEnable, bool bDivideByTwo );

	// Sets up hw morphing state for the vertex shader
	void SetHWMorphVertexShaderState( int nDimConst, int nSubrectConst, VertexTextureSampler_t morphSampler );

	BlendType_t EvaluateBlendRequirements( int textureVar, bool isBaseTexture, int detailTextureVar = -1 );

	// Helper for setting up flashlight constants
	void SetFlashlightVertexShaderConstants( bool bBump, int bumpTransformVar, bool bDetail, int detailScaleVar, bool bSetTextureTransforms );

	struct DrawFlashlight_dx90_Vars_t
	{
		DrawFlashlight_dx90_Vars_t() 
		{ 
			// set all ints to -1
			memset( this, 0xFF, sizeof(DrawFlashlight_dx90_Vars_t) ); 
			// set all bools to a default value.
			m_bBump = false;
			m_bLightmappedGeneric = false;
			m_bWorldVertexTransition = false;
			m_bTeeth = false;
			m_bSSBump = false;
			m_fSeamlessScale = 0.0;
		}
		bool m_bBump;
		bool m_bLightmappedGeneric;
		bool m_bWorldVertexTransition;
		bool m_bTeeth;
		int m_nBumpmapVar;
		int m_nBumpmapFrame;
		int m_nBumpTransform;
		int m_nFlashlightTextureVar;
		int m_nFlashlightTextureFrameVar;
		int m_nBaseTexture2Var;
		int m_nBaseTexture2FrameVar;
		int m_nBumpmapVar2;
		int m_nBumpmapFrame2;
		int m_nBumpTransform2;
		int m_nDetailVar;
		int m_nDetailScale;
		int m_nDetailTextureCombineMode;
		int m_nDetailTextureBlendFactor;
		int m_nDetailTint;
		int m_nDetailVar2;
		int m_nDetailScale2;
		int m_nDetailTextureBlendFactor2;
		int m_nDetailTint2;
		int m_nTeethForwardVar;
		int m_nTeethIllumFactorVar;
		int m_nAlphaTestReference;
		bool m_bSSBump;
		float m_fSeamlessScale;								// 0.0 = not seamless
		int m_nLayerTint1;
		int m_nLayerTint2;
	};
	void DrawFlashlight_dx90( IMaterialVar** params, 
		IShaderDynamicAPI *pShaderAPI, IShaderShadow* pShaderShadow, DrawFlashlight_dx90_Vars_t &vars );

	void HashShadow2DJitter( const float fJitterSeed, float *fU, float* fV );

	//Alpha tested materials can end up leaving garbage in the dest alpha buffer if they write depth. 
	//This pass fills in the areas that passed the alpha test with depth in dest alpha 
	//by writing only equal depth pixels and only if we should be writing depth to dest alpha
	void DrawEqualDepthToDestAlpha( void );
	
private:
	// Converts a color + alpha into a vector4
	void ColorVarsToVector( int colorVar, int alphaVar, Vector4D &color );
};

FORCEINLINE bool IsSRGBDetailTexture( int nMode )
{
	return	( nMode == DETAIL_BLEND_MODE_DETAIL_OVER_BASE ) ||
			( nMode == DETAIL_BLEND_MODE_FADE ) ||
			( nMode == DETAIL_BLEND_MODE_BASE_OVER_DETAIL );
}

FORCEINLINE bool IsSRGBDecalTexture( int nMode )
{
	return	(nMode == DECAL_BLEND_MODE_DECAL_ALPHA);
}

FORCEINLINE char * GetFlashlightTextureFilename()
{
	//if ( !IsX360() && ( g_pHardwareConfig->SupportsBorderColor() ) )
	//{
	//	return "effects/flashlight001_border";
	//}
	//else
	{
		return "effects/flashlight001";
	}
}

extern ConVar r_flashlightbrightness;

FORCEINLINE void SetFlashLightColorFromState( FlashlightState_t const &state, IShaderDynamicAPI *pShaderAPI, bool bSinglePassFlashlight, int nPSRegister=28, bool bFlashlightNoLambert=false )
{
	// Old code
	//float flToneMapScale = ( ShaderApiFast( pShaderAPI )->GetToneMappingScaleLinear() ).x;
	//float flFlashlightScale = 1.0f / flToneMapScale;

	// Fix to old code to keep flashlight from ever getting brighter than 1.0
	//float flToneMapScale = ( ShaderApiFast( pShaderAPI )->GetToneMappingScaleLinear() ).x;
	//if ( flToneMapScale < 1.0f )
	//	flToneMapScale = 1.0f;
	//float flFlashlightScale = 1.0f / flToneMapScale;

	float flFlashlightScale = r_flashlightbrightness.GetFloat();

	if ( !g_pHardwareConfig->GetHDREnabled() )
	{
		// Non-HDR path requires 2.0 flashlight
		flFlashlightScale = 2.0f;
	}

	// DX10 hardware and single pass flashlight require a hack scalar since the flashlight is added in linear space
	if ( ( g_pHardwareConfig->UsesSRGBCorrectBlending() ) || ( bSinglePassFlashlight ) )
	{
		flFlashlightScale *= 2.5f; // Magic number that works well on the 360 and NVIDIA 8800
	}

	flFlashlightScale *= state.m_fBrightnessScale;

	// Generate pixel shader constant
	float const *pFlashlightColor = state.m_Color;
	float vPsConst[4] = { flFlashlightScale * pFlashlightColor[0], flFlashlightScale * pFlashlightColor[1], flFlashlightScale * pFlashlightColor[2], pFlashlightColor[3] };
	vPsConst[3] = bFlashlightNoLambert ? 2.0f : 0.0f; // This will be added to N.L before saturate to force a 1.0 N.L term

	// Red flashlight for testing
	//vPsConst[0] = 0.5f; vPsConst[1] = 0.0f; vPsConst[2] = 0.0f;

	ShaderApiFast( pShaderAPI )->SetPixelShaderConstant( nPSRegister, ( float * )vPsConst );
}

FORCEINLINE float ShadowAttenFromState( FlashlightState_t const &state )
{
	// DX10 requires some hackery due to sRGB/blend ordering change from DX9, which makes the shadows too light
	if ( g_pHardwareConfig->UsesSRGBCorrectBlending() )
		return state.m_flShadowAtten * 0.1f; // magic number

	return state.m_flShadowAtten;
}

FORCEINLINE float ShadowFilterFromState( FlashlightState_t const &state )
{
	// We developed shadow maps at 1024, so we expect the penumbra size to have been tuned relative to that
	return state.m_flShadowFilterSize / 1024.0f;
}


FORCEINLINE void SetupUberlightFromState( IShaderDynamicAPI *pShaderAPI, FlashlightState_t const &state )
{
	// Bail if we can't do ps30 or we don't even want an uberlight
	if ( !g_pHardwareConfig->HasFastVertexTextures() || !state.m_bUberlight || !pShaderAPI )
		return;

	UberlightState_t u = state.m_uberlightState;

	// Set uberlight shader parameters as function of user controls from UberlightState_t
	Vector4D vSmoothEdge0		= Vector4D( 0.0f,			u.m_fCutOn - u.m_fNearEdge,	u.m_fCutOff,				0.0f );
	Vector4D vSmoothEdge1		= Vector4D( 0.0f,			u.m_fCutOn,					u.m_fCutOff + u.m_fFarEdge,	0.0f );
	Vector4D vSmoothOneOverW	= Vector4D( 0.0f,			1.0f / u.m_fNearEdge,		1.0f / u.m_fFarEdge,		0.0f );
	Vector4D vShearRound		= Vector4D( u.m_fShearx,	u.m_fSheary,				2.0f / u.m_fRoundness,	   -u.m_fRoundness / 2.0f );
	Vector4D vaAbB				= Vector4D( u.m_fWidth,		u.m_fWidth + u.m_fWedge,	u.m_fHeight,				u.m_fHeight + u.m_fHedge );

	ShaderApiFast( pShaderAPI )->SetPixelShaderConstant( PSREG_UBERLIGHT_SMOOTH_EDGE_0, vSmoothEdge0.Base(), 1 );
	ShaderApiFast( pShaderAPI )->SetPixelShaderConstant( PSREG_UBERLIGHT_SMOOTH_EDGE_1, vSmoothEdge1.Base(), 1 );
	ShaderApiFast( pShaderAPI )->SetPixelShaderConstant( PSREG_UBERLIGHT_SMOOTH_EDGE_OOW, vSmoothOneOverW.Base(), 1 );
	ShaderApiFast( pShaderAPI )->SetPixelShaderConstant( PSREG_UBERLIGHT_SHEAR_ROUND, vShearRound.Base(), 1 );
	ShaderApiFast( pShaderAPI )->SetPixelShaderConstant( PSREG_UBERLIGHT_AABB, vaAbB.Base(), 1 );

	QAngle angles;
	QuaternionAngles( state.m_quatOrientation, angles );

	// World to Light's View matrix
	matrix3x4_t viewMatrix, viewMatrixInverse;
	AngleMatrix( angles, state.m_vecLightOrigin, viewMatrixInverse );
	MatrixInvert( viewMatrixInverse, viewMatrix );
	ShaderApiFast( pShaderAPI )->SetPixelShaderConstant( PSREG_UBERLIGHT_WORLD_TO_LIGHT, viewMatrix.Base(), 4 );
}


// convenient material variable access functions for helpers to use.
FORCEINLINE bool IsTextureSet( int nVar, IMaterialVar **params )
{
	return ( nVar != -1 ) && ( params[nVar]->IsTexture() );
}

FORCEINLINE bool IsBoolSet( int nVar, IMaterialVar **params )
{
	return ( nVar != -1 ) && ( params[nVar]->GetIntValue() );
}

FORCEINLINE int GetIntParam( int nVar, IMaterialVar **params, int nDefaultValue = 0 )
{
	return ( nVar != -1 ) ? ( params[nVar]->GetIntValue() ) : nDefaultValue;
}

FORCEINLINE float GetFloatParam( int nVar, IMaterialVar **params, float flDefaultValue = 0.0 )
{
	return ( nVar != -1 ) ? ( params[nVar]->GetFloatValue() ) : flDefaultValue;
}

FORCEINLINE void InitFloatParam( int nIndex, IMaterialVar **params, float flValue )
{
	if ( (nIndex != -1) && !params[nIndex]->IsDefined() )
	{
		params[nIndex]->SetFloatValue( flValue );
	}
}

FORCEINLINE void InitIntParam( int nIndex, IMaterialVar **params, int nValue )
{
	if ( (nIndex != -1) && !params[nIndex]->IsDefined() )
	{
		params[nIndex]->SetIntValue( nValue );
	}
}

FORCEINLINE void InitVecParam( int nIndex, IMaterialVar **params, float x, float y )
{
	if ( (nIndex != -1) && !params[nIndex]->IsDefined() )
	{
		params[nIndex]->SetVecValue( x, y );
	}
}

FORCEINLINE void InitVecParam( int nIndex, IMaterialVar **params, float x, float y, float z )
{
	if ( (nIndex != -1) && !params[nIndex]->IsDefined() )
	{
		params[nIndex]->SetVecValue( x, y, z );
	}
}

FORCEINLINE void InitVecParam( int nIndex, IMaterialVar **params, float x, float y, float z, float w )
{
	if ( (nIndex != -1) && !params[nIndex]->IsDefined() )
	{
		params[nIndex]->SetVecValue( x, y, z, w );
	}
}

// Did we launch with -tools
bool ToolsEnabled();

class ConVar;

#ifdef _DEBUG
extern ConVar mat_envmaptintoverride;
extern ConVar mat_envmaptintscale;
#endif


#endif // BASEVSSHADER_H