float4x4 gWorld : WORLD;
float4x4 gWorldViewProj : WORLDVIEWPROJECTION; 
float3 gLightDirection = float3(-0.577f, -0.577f, 0.577f);
float4x4 gLightVP;
float4x4 gBones[70];
float gShadowStrength = 0.4f;

Texture2D gDiffuseMap;
Texture2D gShadowMap;

SamplerState samLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;// or Mirror or Clamp or Border
    AddressV = Wrap;// or Mirror or Clamp or Border
};

SamplerComparisonState samComparison
{
   // sampler state
   Filter = COMPARISON_MIN_MAG_MIP_LINEAR;
   AddressU = MIRROR;
   AddressV = MIRROR;
 
   // sampler comparison state
   ComparisonFunc = LESS_EQUAL;
};

RasterizerState Solid
{
	FillMode = SOLID;
	CullMode = FRONT;
};

struct VS_INPUT
{
	float3 pos : POSITION;
	float3 normal : NORMAL;
	float2 texCoord : TEXCOORD;
	//BlendWeights & BlendIndices
	float4 blendIndex : BLENDINDICES;
	float4 blendWeight : BLENDWEIGHTS;
};

struct VS_OUTPUT
{
	float4 pos : SV_POSITION;
	float3 normal : NORMAL;
	float2 texCoord : TEXCOORD;
	float4 lpos : TEXCOORD1;
};

float2 texOffset( int u, int v )
{
	int width, height;
	gShadowMap.GetDimensions(width, height);
    return float2( u * 1.0f/width, v * 1.0f/height );
}

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
VS_OUTPUT VS(VS_INPUT input){

	VS_OUTPUT output;

	float4 originalPosition = float4(input.pos, 1);
	float4 transformedPosition = 0;
	float3 transformedNormal = 0;

	//Skinning Magic...
	for(int i = 0; i < 4; ++i)
	{
		int boneIdx = input.blendIndex[i];
		if(boneIdx > -1)
		{
			transformedPosition += input.blendWeight[i] * mul(originalPosition, gBones[boneIdx]);

			transformedNormal += input.blendWeight[i] * mul(input.normal, (float3x3)gBones[boneIdx]);

			transformedPosition.w = 1;
		}
	}

	output.pos = mul(transformedPosition, gWorldViewProj);
	output.normal = mul(transformedNormal, (float3x3)gWorld);

	output.texCoord = input.texCoord;

	//LPOS
	output.lpos = mul(float4(input.pos, 1), mul(gWorld, gLightVP));

	return output;
}

//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS(VS_OUTPUT input) : SV_TARGET
{
	float4 diffuseColor = gDiffuseMap.Sample( samLinear,input.texCoord );
	float3 color_rgb= diffuseColor.rgb;
	float color_a = diffuseColor.a;
	
	//HalfLambert Diffuse :)
	float diffuseStrength = dot(input.normal, -gLightDirection);
	diffuseStrength = diffuseStrength * 0.5 + 0.5;
	diffuseStrength = saturate(diffuseStrength);
	color_rgb = color_rgb * diffuseStrength;

	input.lpos.xyz /= input.lpos.w;

	if( input.lpos.x < -1.0f || input.lpos.x > 1.0f || input.lpos.y < -1.0f || input.lpos.y > 1.0f || input.lpos.z < 0.0f  || input.lpos.z > 1.0f ) 
	{
		return float4( 1.0f, 0.0f, 1.0f, 1.0f );
	}

	input.lpos.x = input.lpos.x / 2.0f + 0.5f;
	input.lpos.y = input.lpos.y / -2.0f + 0.5f;

	input.lpos.z -= 0.0001f;

	float sum;
	float x, y;
	for (y = -2; y <=2; y += 1.0f)
    {
        for (x = -2; x <= 2; x += 1.0f)
        {
            sum += gShadowMap.SampleCmpLevelZero(samComparison, input.lpos.xy + texOffset(x,y), input.lpos.z );
        }
    }
    float shadowFactor = sum / 25.0f;
    shadowFactor = saturate(shadowFactor + 1 - gShadowStrength);

	float3 light = normalize(input.lpos.xyz - input.pos.xyz);
	float ndotl = saturate(dot(normalize(input.normal), light)) + 1 - gShadowStrength;
	ndotl = saturate(ndotl);
	return float4( shadowFactor * color_rgb * ndotl, color_a);
}

//--------------------------------------------------------------------------------------
// Technique
//--------------------------------------------------------------------------------------
technique11 Default
{
    pass P0
    {
		SetRasterizerState(Solid);
		SetVertexShader( CompileShader( vs_4_0, VS() ) );
		SetGeometryShader( NULL );
		SetPixelShader( CompileShader( ps_4_0, PS() ) );
    }
}

