
#include "standardfuncs.fxh"

float4x4 WorldMatrix;
float3 CameraPosition;
float4x4 matBones[45] : Bones;
float3 LightDirection;
float Time;
float4 TextureOffset;
float4 PrimaryColor   = float4(0.8, 0.0, 0, 1);
float4 SecondaryColor = float4(0.0, 0.7, 0, 1);
float2 TextureSqueze =float2( 0.9882812, 0.9882812 ); //22 flags * 92 pixels does not 2048 make... TODO, move this calcualtion to code

float vScale = 1.0;
float FOW = 1.0;

// debug flags
//#define DEBUG_ONLY_SPECULAR
//#define DEBUG_ONLY_DIFFUSE
//#define DEBUG_ONLY_COLORMAP
//#define DEBUG_SHOW_SPECULARMAP
//#define DEBUG_SHOW_TEXCOORDS

// select lighting model to use
//#define LIGHTMODEL_WRAP
//#define LIGHTMODEL_HALFLAMBERT
#define LIGHTMODEL_PHONG

const int SKINNING_INFLUENCES = 2;

float SPECULAR_POWER = 20;
float SPECULARITY = 1.25;
const float WRAP = 0.7;
const float AMBIENT = 0.5;
const float INTENSITY = 1.75;
const float3 LIGHT_OFFSET = float3(-250, -500, 200);
//float3 LIGHT_OFFSET = float3(0, 0, 100);



// Flag specific
const float ANIMATION_SPEED = 0.5;
const float INTENSITY_FLAG = 0.9;
const float3 LIGHT_OFFSET_FLAG = float3(-250, -500, 100);
const float FLAG_GRAYNESS = 0.3;
float SPECULAR_POWER_FLAG = 10;
float SPECULARITY_FLAG = 0.1;

texture tex0 : DiffuseTexture;
sampler2D DiffuseMap = 
sampler_state 
{
    texture = <tex0>;
    AddressU  = Wrap;        
    AddressV  = Wrap;
    MipFilter = Linear;
    MagFilter = Linear;
	MinFilter = Anisotropic;
    
    MaxAnisotropy = 4;
};

texture tex1 : Texture1;
sampler2D SpecularMap = 
sampler_state 
{
    texture = <tex1>;
    AddressU  = Wrap;        
    AddressV  = Wrap;
    MipFilter = Linear;
    MinFilter = Linear;
    MagFilter = Linear;
};

texture tex2 : Texture2;
sampler2D NormalMap = 
sampler_state 
{
    texture = <tex2>;
    AddressU  = Wrap;        
    AddressV  = Wrap;
    MipFilter = Linear;
    MagFilter = Linear;
	MinFilter = Linear;
};

texture tex3;
sampler2D FlagMap = 
sampler_state 
{
    texture = <tex3>;
    AddressU  = Clamp;        
    AddressV  = Clamp;
    MipFilter = None;
    MagFilter = Linear;
	MinFilter = Linear;
};

struct VS_INPUT
{
    float4 vPosition   : POSITION;
    float3 vNormal     : NORMAL;
	float4 vTangent    : TANGENT;
	float2 vTexCoord0  : TEXCOORD0;
	float4 boneIndices : BLENDINDICES;
    float4 boneWeights : BLENDWEIGHT;
};

struct VS_INPUT_STATIC
{
    float4 vPosition   : POSITION;
    float3 vNormal     : NORMAL;
	float4 vTangent    : TANGENT;
	float2 vTexCoord0  : TEXCOORD0;
};

struct VS_OUTPUT
{
    float4 vPosition  : POSITION;
	float2 vTexCoord0 : TEXCOORD0;
	float3 Normal     : TEXCOORD1;
	float3 LightDirection : TEXCOORD2;
	float3 EyeVec     : TEXCOORD3;
	float3 vPos       : TEXCOORD4;
};

struct VS_INPUT_TAB
{
    float4 vPosition   : POSITION;
    float3 vNormal     : NORMAL;
	float4 vTangent    : TANGENT;
	float2 vTexCoord0  : TEXCOORD0;
	float2 vTexCoord1  : TEXCOORD1;
	float4 boneIndices : BLENDINDICES;
    float4 boneWeights : BLENDWEIGHT;
};

struct VS_INPUT_TAB_STATIC
{
    float4 vPosition   : POSITION;
    float3 vNormal     : NORMAL;
	float4 vTangent    : TANGENT;
	float2 vTexCoord0  : TEXCOORD0;
	float2 vTexCoord1  : TEXCOORD1;
};


struct VS_OUTPUT_TAB
{
    float4 vPosition  : POSITION;
	float2 vTexCoord0 : TEXCOORD0;
	float2 vTexCoord1 : TEXCOORD1;
	float3 Normal     : TEXCOORD2;
	float3 LightDirection : TEXCOORD3;
	float3 EyeVec	  : TEXCOORD4;
	float3 vPos       : TEXCOORD5;
	float3 vTangent		: TEXCOORD6;
};


float3 ApplyFOWColor( float3 c ) 
{
	const float3 GREYIFY = float3( 0.212671, 0.715160, 0.072169 );
	float Grey = dot( c.rgb, GREYIFY );
	return lerp( Grey.rrr * 0.4, c.rgb, FOW > 0.5 ? 1.0 : 0.5 );
}

VS_OUTPUT SkinnedAvatarVS(const VS_INPUT v )
{
	VS_OUTPUT Out = (VS_OUTPUT)0;
		
	float4 skinnedPosition = (float4)0;
	float4 skinnedNormal   = (float4)0;
	
	float4 vPosition = float4(v.vPosition.xyz, 1.0);
	
	float3 vNormal = v.vNormal;
	// skinning
	for( int i = 0; i < SKINNING_INFLUENCES; ++i )
    {
    	float4x4 mat = matBones[ v.boneIndices[i] ];

		float4 offset = mul( vPosition, mat ) * v.boneWeights[i];
		skinnedPosition += offset;
		
		offset = mul( vNormal, mat )  * v.boneWeights[i];
		skinnedNormal += offset;
	}
	
	Out.LightDirection = -normalize(skinnedPosition - CameraPosition + LIGHT_OFFSET );
	Out.EyeVec = -normalize(skinnedPosition - (CameraPosition) );
	
	Out.vPos = skinnedPosition.xyz;
	Out.vPosition = mul(skinnedPosition, ViewProjectionMatrix );
	Out.vTexCoord0 = v.vTexCoord0;
	Out.Normal  = normalize(skinnedNormal);

	return Out;
}

VS_OUTPUT StaticAvatarVS(const VS_INPUT_STATIC v )
{
	VS_OUTPUT Out = (VS_OUTPUT)0;
			
	Out.vPosition = float4(v.vPosition.xyz * vScale, 1.0);
	Out.vPosition = mul(Out.vPosition, WorldMatrix );
	
	Out.LightDirection = -normalize(Out.vPosition - CameraPosition + LIGHT_OFFSET );
	Out.EyeVec = -normalize(Out.vPosition - (CameraPosition) );

	Out.vPos = Out.vPosition.xyz;
	Out.vPosition = mul(Out.vPosition, ViewProjectionMatrix );
	
	Out.vTexCoord0 = v.vTexCoord0;
	Out.Normal  = normalize( mul( v.vNormal, WorldMatrix ) );

	return Out;
}


float4 SkinnedAvatarPS( VS_OUTPUT In ) : COLOR
{
	
	float4 vColor = tex2D( DiffuseMap, In.vTexCoord0 );
	float3 vSpecColor = tex2D( SpecularMap, In.vTexCoord0 ).rgb;
	float3 vNormal = normalize(In.Normal);

	// add some individual color
	vColor.rgb = lerp(vColor.rgb, 
	        vColor.rgb * (1.0 * PrimaryColor.rgb * vSpecColor.g), 
		    vSpecColor.g);

	vColor.rgb = lerp(vColor.rgb,  
	        vColor.rgb * (1.0 * SecondaryColor.rgb * vSpecColor.b), 
			vSpecColor.b);
	
	float3 L = normalize(In.LightDirection);
	float3 E = normalize(In.EyeVec);
	float3 halfVec = normalize(E + L);
	float  NdotL = max(0 , dot(vNormal,L));
	
	#ifdef LIGHTMODEL_WRAP
	float diffuse = saturate((NdotL + WRAP) / (1 + WRAP)) * INTENSITY;
	#endif
	#ifdef LIGHTMODEL_HALFLAMBERT
	float diffuse = pow(0.5 * NdotL + 0.5, 2) * INTENSITY; 
	#endif
	#ifdef LIGHTMODEL_PHONG
	float diffuse = NdotL * INTENSITY + AMBIENT;
	#endif
	
    float NDotH = dot(vNormal, halfVec);
	float vMax = saturate(NDotH );
	float specular = pow( vMax, SPECULAR_POWER );

	vColor.rgb *= diffuse;
	
	vColor.rgb = ApplyDistanceFog( vColor.rgb, In.vPos );

	specular *= vSpecColor.r * SPECULARITY; // mask

	vColor.rgb = ComposeSpecular( vColor.rgb, specular );
		
	return vColor;
}


float2 GetTexCoordsInAtlas(float2 TexCoord)
{
	return float2( TexCoord.x * TextureOffset.x + TextureOffset.z,
				   TexCoord.y * TextureOffset.y + TextureOffset.w );
}

VS_OUTPUT SkinnedAvatarVSFlag(const VS_INPUT v )
{
	VS_OUTPUT Out = (VS_OUTPUT)0;
	
	float4 skinnedPosition = (float4)0;
	float4 skinnedNormal   = (float4)0;
	float4 skinnedTangent  = (float4)0;
	
	float4 vPosition = float4(v.vPosition.xyz, 1.0);

	// skinning
	for( int i = 0; i < SKINNING_INFLUENCES; ++i )
    {
    	float4x4 mat = matBones[ v.boneIndices[i] ];

		float4 offset = mul( vPosition, mat ) * v.boneWeights[i];
		skinnedPosition += offset;
		
		offset = mul( v.vNormal, mat )  * v.boneWeights[i];
		skinnedNormal += offset;

		offset = mul( v.vTangent, mat ) * v.boneWeights[i];
		skinnedTangent += offset;
	}
	
	skinnedNormal  = normalize(skinnedNormal);
	skinnedTangent = normalize(skinnedTangent);
	float3 binormal = cross(skinnedTangent.xyz, skinnedNormal.xyz ) * v.vTangent.w;
	normalize(binormal);
	
	// transform light direction into tangent space
	float3x3 matTBN = float3x3(skinnedTangent.xyz, 
	                           binormal,
							   skinnedNormal.xyz);
	Out.LightDirection = mul(matTBN, -normalize(skinnedPosition - CameraPosition + LIGHT_OFFSET_FLAG ));
	Out.EyeVec = mul(matTBN, -normalize(skinnedPosition - (CameraPosition) ));
	
	Out.vPosition = mul(skinnedPosition, ViewProjectionMatrix );
	Out.vTexCoord0 = v.vTexCoord0;
	
	return Out;
}

float4 SkinnedAvatarPSFlag( VS_OUTPUT In ) : COLOR
{
	float t = frac(Time * ANIMATION_SPEED);
	float2 NormalCoord = float2(In.vTexCoord0.x - t, In.vTexCoord0.y );

	float4 vColor = tex2D( DiffuseMap, GetTexCoordsInAtlas(In.vTexCoord0) );
	float3 vNormal = normalize( tex2D( NormalMap, NormalCoord ).rgb * 2.0 - 1 );
		
	float3 L = normalize(In.LightDirection);
	float3 E = normalize(In.EyeVec);
	float3 halfVec = normalize(E + L);
	float  NdotL = dot(vNormal, L);
		
	#ifdef LIGHTMODEL_WRAP
	float diffuse = saturate((saturate(NdotL) + WRAP) / (1 + WRAP)) * INTENSITY_FLAG;
	#endif
	#ifdef LIGHTMODEL_HALFLAMBERT
	float diffuse = pow(0.5 * saturate(NdotL) + 0.5, 2) * INTENSITY_FLAG; 
	#endif
	#ifdef LIGHTMODEL_PHONG
	float diffuse = saturate(NdotL) * INTENSITY_FLAG + AMBIENT;
	#endif
	
	    
 	float  specular = pow( saturate( dot(vNormal, halfVec) ), SPECULAR_POWER_FLAG );
    if( NdotL <= 0 )
    {
       specular = 0;
    }
	
	specular *= SPECULARITY_FLAG; // mask
		
	vColor.rgb = vColor.rgb  * saturate(diffuse) +  float3(0.0,0.0,0.0);
	float Grey = dot( vColor.rgb, float3( 0.212671f, 0.715160f, 0.072169f ) );
	return float4(lerp( vColor.rgb, Grey.rrr, FLAG_GRAYNESS ) + specular, vColor.a);
}


float4 SkinnedAvatarPSShadow( VS_OUTPUT In ) : COLOR
{
	
	float4 vColor = tex2D( DiffuseMap, In.vTexCoord0 );
	return vColor;
}

VS_OUTPUT_TAB StaticAvatarVSTabard(const VS_INPUT_TAB_STATIC v )
{
	VS_OUTPUT_TAB Out = (VS_OUTPUT_TAB)0;

	Out.vPosition = float4(v.vPosition.xyz * vScale, 1.0);
	float4x4 Rot = { ViewMatrix._m00_m10_m20, 0.0f,
                     ViewMatrix._m01_m11_m21, 0.0f,
					 ViewMatrix._m02_m12_m22, 0.0f,
					 0.0f, 0.0f, 0.0f, 1.0f }; 

	Out.vPosition = mul(Out.vPosition, Rot );
	Out.vPosition = mul(Out.vPosition, WorldMatrix );

	float4 lightpos = float4( 0.0,300.0,150.0,1.0);
	
	Out.LightDirection = -normalize(Out.vPosition - lightpos );
	Out.EyeVec = -normalize(Out.vPosition - (CameraPosition) );

	Out.vPos = Out.vPosition.xyz;
	Out.vPosition = mul(Out.vPosition, ViewProjectionMatrix );

	Out.vTexCoord0 = v.vTexCoord0;
	Out.vTexCoord1 = v.vTexCoord1;
	Out.Normal  = normalize( mul( v.vNormal, Rot ) );
	Out.vTangent  = normalize( mul( v.vTangent, Rot ) );

	return Out;
}



VS_OUTPUT_TAB SkinnedAvatarVSTabard(const VS_INPUT_TAB v )
{
	
	VS_OUTPUT_TAB Out = (VS_OUTPUT_TAB)0;
		
	float4 skinnedPosition = (float4)0;
	float4 skinnedNormal   = (float4)0;
	float4 skinnedTangent   = (float4)0;
	
	float4 vPosition = float4(v.vPosition.xyz, 1.0);
	
	float3 vNormal = v.vNormal;
	float3 vTangent = v.vTangent;
	// skinning
	for( int i = 0; i < SKINNING_INFLUENCES; ++i )
    {
    	float4x4 mat = matBones[ v.boneIndices[i] ];

		float4 offset = mul( vPosition, mat ) * v.boneWeights[i];
		skinnedPosition += offset;
		offset = float4( mul( vNormal, (float3x3) mat ), 0 ) * v.boneWeights[i];
		
		skinnedNormal += offset;
		
		offset = float4( mul( vTangent, (float3x3) mat ), 0 ) * v.boneWeights[i];
		
		skinnedTangent += offset;
	
	}
	
	float4 lightpos = float4( 0.0, 300.0,150.0,1.0);	
	Out.LightDirection = -normalize(skinnedPosition - lightpos );

	//Out.LightDirection = -normalize(skinnedPosition - CameraPosition + LIGHT_OFFSET );
	Out.EyeVec = -normalize(skinnedPosition - (CameraPosition) );
	
	Out.vPos = skinnedPosition.xyz;
	Out.vPosition = mul(skinnedPosition, ViewProjectionMatrix );
	Out.vTexCoord0 = v.vTexCoord0;
	Out.vTexCoord1 = v.vTexCoord1;
	Out.Normal  = normalize(skinnedNormal);
	Out.vTangent = normalize( skinnedTangent );
	return Out;
}

float4 SkinnedAvatarPSTabard( VS_OUTPUT_TAB In ) : COLOR
{
	float4 vColor = tex2D( DiffuseMap, In.vTexCoord0 );
	float3 vSpecColor = tex2D( SpecularMap, In.vTexCoord0 ).rgb;
	float3 vTabardColor = tex2D( FlagMap, GetTexCoordsInAtlas( In.vTexCoord1 ) ).rgb;
	
	float t = frac(Time * ANIMATION_SPEED);
	float2 NormalCoord = float2(In.vTexCoord0.x - t, In.vTexCoord0.y );
	
	float3 vNormal = normalize( In.Normal);
		
	float3 L = normalize(In.LightDirection);
	float3 E = normalize(In.EyeVec);
	float3 halfVec = normalize(E + L);
	float  NdotL = max(0 , dot(vNormal,L));
	
	#ifdef LIGHTMODEL_WRAP
	float diffuse = saturate((NdotL + WRAP) / (1 + WRAP)) * INTENSITY;
	#endif
	#ifdef LIGHTMODEL_HALFLAMBERT
	float diffuse = pow(0.5 * NdotL + 0.5, 2) * INTENSITY; 
	#endif
	#ifdef LIGHTMODEL_PHONG
	float diffuse = NdotL * INTENSITY + AMBIENT;
	#endif
	
    float NDotH = dot(vNormal, halfVec);
	float vMax = saturate(NDotH );
	float specular = pow( vMax, SPECULAR_POWER );
	
	specular *= vSpecColor.r * SPECULARITY; // mask
	
	float3 vFinal = vColor.rgb * ( 1 - vColor.a ) + vColor.rgb  * vColor.a * vTabardColor;

	// TODO: Standard lighting
	vFinal *= saturate(diffuse);
	
	vFinal = ApplyDistanceFog( vFinal, In.vPos );
	
	
	return float4( ComposeSpecular( vFinal, specular ), 1);
}

/////////////////////////////////////////////////////
float4 SkinnedAvatarPSTabardNormal( VS_OUTPUT_TAB In ) : COLOR
{
	float4 vColor = tex2D( DiffuseMap, In.vTexCoord0 );
	float3 vSpecColor = tex2D( SpecularMap, In.vTexCoord0 ).rgb;
	float3 vTabardColor = tex2D( FlagMap, GetTexCoordsInAtlas( In.vTexCoord1 ) ).rgb;
	
	float t = frac(Time * ANIMATION_SPEED);
	float2 NormalCoord = float2(In.vTexCoord0.x - t, In.vTexCoord0.y );
	
	float3 vBaseNormal = normalize( In.Normal );
	float3 vBaseTangent = normalize( In.vTangent );
	float3 vBaseBinormal = cross( vBaseNormal, vBaseTangent );
	
	float3 vNormal = normalize( tex2D( NormalMap, NormalCoord ).rgb * 2.0 - 1 );
	//return float4( -vBaseNormal, 1 );
	
	//vNormal = -normalize(/*vNormal +*/ vBaseBinormal);
	
	vNormal = vBaseTangent * vNormal.r - vBaseBinormal * vNormal.g + vBaseNormal * vNormal.b;
	
	float3 L = normalize(In.LightDirection);
	float3 E = normalize(In.EyeVec);
	float3 halfVec = normalize(E + L);
	float  NdotL = max(0 , dot(vNormal,L));
	
	#ifdef LIGHTMODEL_WRAP
	float diffuse = saturate((NdotL + WRAP) / (1 + WRAP)) * INTENSITY;
	#endif
	#ifdef LIGHTMODEL_HALFLAMBERT
	float diffuse = pow(0.5 * NdotL + 0.5, 2) * INTENSITY; 
	#endif
	#ifdef LIGHTMODEL_PHONG
	float diffuse = NdotL * INTENSITY + AMBIENT;
	#endif
	

	float NDotH = dot(vNormal, halfVec);
	float vMax = saturate(NDotH );
	float specular = pow( vMax, SPECULAR_POWER );
	
	specular *= vSpecColor.r * SPECULARITY; // mask
	
	float3 vFinal = vColor.rgb * ( 1 - vColor.a ) + vColor.rgb  * vColor.a * vTabardColor;
	
	// TODO: Standard lighting
	vFinal *= saturate(diffuse);

	vFinal = ApplyDistanceFog( vFinal, In.vPos );
	
	//vFinal.rgb = 1 - In.vPos.xyz / 1000;
	//return float4( vFinal, 1 );
	return float4( ComposeSpecular( vFinal, specular ), 1);
}

float4 SkinnedAvatarPSSmoke( VS_OUTPUT In ) : COLOR
{
	float t = frac(Time * ANIMATION_SPEED);
	float vAlpha = tex2D( DiffuseMap, In.vTexCoord0 ).a;
	float4 vColor = tex2D( DiffuseMap, float2(In.vTexCoord0.x, In.vTexCoord0.y + t ) );
	vColor.a =  vAlpha;
	return vColor;
}

/////////////////////////////////////////////////////

technique StaticStandard
{
	pass p0
	{	
		MipMapLodBias[0] = -1;
		VertexShader = compile vs_2_0 StaticAvatarVS();
		PixelShader = compile ps_2_0 SkinnedAvatarPS();
	}
}

technique Standard
{
	pass p0
	{
		MipMapLodBias[0] = -1;
		
		VertexShader = compile vs_2_0 SkinnedAvatarVS();
		PixelShader = compile ps_2_0 SkinnedAvatarPS();
	}
}

technique StaticTabard
{
	pass p0
	{		
		VertexShader = compile vs_2_0 StaticAvatarVSTabard();
		PixelShader = compile ps_2_0 SkinnedAvatarPSTabard();
	}
}

technique Tabard
{
	pass p0
	{
		MipMapLodBias[0] = -1;
		
		VertexShader = compile vs_2_0 SkinnedAvatarVSTabard();
		PixelShader = compile ps_2_0 SkinnedAvatarPSTabard();
	}
}

technique TabardNormal
{
	pass p0
	{
		MipMapLodBias[0] = -1;
		
		VertexShader = compile vs_2_0 SkinnedAvatarVSTabard();
		PixelShader = compile ps_2_0 SkinnedAvatarPSTabardNormal();
	}
}

technique Flag
{
	pass p0
	{
		VertexShader = compile vs_2_0 SkinnedAvatarVSFlag();
		PixelShader = compile ps_2_0 SkinnedAvatarPSFlag();
	}
}


technique Shadow
{
	pass p0
	{
		ZENABLE = True;
		ALPHABLENDENABLE = True;
		ALPHATESTENABLE = False;
		ZWRITEENABLE = False;
		VertexShader = compile vs_2_0 SkinnedAvatarVS();
		PixelShader = compile ps_2_0 SkinnedAvatarPSShadow();
	}
}

technique Smoke
{
	pass p0
	{
		ZENABLE = True;
		ZWRITEENABLE = False;
		ALPHABLENDENABLE = True;
		ALPHATESTENABLE = False;
				
		VertexShader = compile vs_2_0 SkinnedAvatarVS();
		PixelShader = compile ps_2_0 SkinnedAvatarPSSmoke();
	}
}
