#include "_shared.fx"
#include "_lighting.fx"

// parameters ------------------------------------------
float4x4 g_mWorld         : WORLD;
float4x4 g_mView          : VIEW;
float4x4 g_mProjection    : PROJECTION;

float4x4 g_mViewInverse   : VIEWINVERSE;
float4x4 g_mWorldInverse  : WORLDINVERSE;


texture2D 	g_tDiffuseMap;
int         g_iTransparency;

texture2D   g_tSpecularMap;
float       g_fSpecularPower;

texture2D 	g_tNormalMap;


	
// ---------------------------------------
sampler2D g_sDiffuseSampler = 
sampler_state
{
	Texture = <g_tDiffuseMap>;
	MinFilter = anisotropic;
	MagFilter = linear;
	MipFilter = linear;
	AddressU = wrap;
	AddressV = wrap;
};

sampler2D g_sSpecularSampler = 
sampler_state
{
	Texture = <g_tSpecularMap>;
	MinFilter = anisotropic;
	MagFilter = linear;
	MipFilter = linear;
	AddressU = wrap;
	AddressV = wrap;
};

sampler2D g_sNormalSampler = 
sampler_state
{
	Texture = <g_tNormalMap>;
	MinFilter = anisotropic;
	MagFilter = linear;
	MipFilter = linear;
	AddressU = wrap;
	AddressV = wrap;
};



struct {
	float3 vNormal   : NORMAL0;
	float3 vTangent  : TANGENT0;
	float3 vBinormal : BINORMAL0;
} E42_REQUIRE_INPUTSTREAMS;

struct VS_INPUT
{
	float3 vPositionOS	: POSITION0;
	float3 vNormalOS	: NORMAL0;
	float3 vTangentOS   : TANGENT0;
	float3 vBinormalOS  : BINORMAL0;
	float2 vTexcoord0	: TEXCOORD0;
};

struct VS_OUTPUT
{
	float4	vPositionPS	    : POSITION0;
	
    float2  vTexCoord       : TEXCOORD0_centroid;

	float4	vPositionSS0    : TEXCOORD1;
	float4	vPositionSS1    : TEXCOORD2;
	float4	vPositionSS2    : TEXCOORD3;
	
	float4  vLightDirTS0    : TEXCOORD4;
	float4  vLightDirTS1    : TEXCOORD5;
	float4  vLightDirTS2    : TEXCOORD6;
	
	float3  vEyeVecTS       : TEXCOORD7;
};

// shaders ---------------------------------------------
VS_OUTPUT
vsMainStatic(VS_INPUT i)
{
	// preshader->
	float4x4 mWorldViewProjection = mul(mul(g_mWorld, g_mView), g_mProjection);
	float3 vViewerPosOS = mul(g_mViewInverse[3], g_mWorldInverse);
	float3 vLightDirOS0 = mul(c_axLights[0].vDirectionWS, g_mWorldInverse);
	float3 vLightDirOS1 = mul(c_axLights[1].vDirectionWS, g_mWorldInverse);
	float3 vLightDirOS2 = mul(c_axLights[2].vDirectionWS, g_mWorldInverse);
	float4x4 mOStoSS0 = mul(g_mWorld, c_axLights[0].mShadowTransform);
	float4x4 mOStoSS1 = mul(g_mWorld, c_axLights[1].mShadowTransform);
	float4x4 mOStoSS2 = mul(g_mWorld, c_axLights[2].mShadowTransform);
	// <-preshader
	
	
	OrthogonalizeTS(i.vTangentOS, i.vBinormalOS, i.vNormalOS);


	VS_OUTPUT o;
	
	o.vPositionPS = mul(float4(i.vPositionOS, 1), mWorldViewProjection);

	o.vTexCoord = i.vTexcoord0;

	o.vPositionSS0 = mul(float4(i.vPositionOS, 1), mOStoSS0); 
	o.vPositionSS1 = mul(float4(i.vPositionOS, 1), mOStoSS1); 
	o.vPositionSS2 = mul(float4(i.vPositionOS, 1), mOStoSS2);
    
    float3x3 mOStoTS = transpose(float3x3(i.vTangentOS, i.vBinormalOS, i.vNormalOS));

    float3 vEyeVecOS = vViewerPosOS - i.vPositionOS;
    o.vEyeVecTS.xyz = mul(vEyeVecOS, mOStoTS);
    o.vEyeVecTS.xyz = normalize(o.vEyeVecTS.xyz); 

	o.vLightDirTS0.xyz = mul(vLightDirOS0, mOStoTS);   o.vLightDirTS0.w = 0;
	o.vLightDirTS1.xyz = mul(vLightDirOS1, mOStoTS);   o.vLightDirTS1.w = 0;
	o.vLightDirTS2.xyz = mul(vLightDirOS2, mOStoTS);   o.vLightDirTS2.w = 0;

	return o;
}


float4
psMainAmbient(VS_OUTPUT i) : COLOR0
{
	float3 vAmbientColor = tex2D(g_sDiffuseSampler, i.vTexCoord);
	vAmbientColor *= c_vAmbientLightColor;
	
	float4 o;
	o.rgb = vAmbientColor;
	o.a = tex2D(g_sDiffuseSampler, i.vTexCoord).a;
	return o;
}


float4
psMainLight(VS_OUTPUT i, uniform int iNumLights) : COLOR0
{
    // calc normal
    float3 vNormalTS = tex2D(g_sNormalSampler, i.vTexCoord) * 2 - 1;
    
	// fix backfacing normals
	if (dot(vNormalTS, i.vEyeVecTS) < 0)
	{
		float3 vTmp = cross(vNormalTS, i.vEyeVecTS);
		vNormalTS = cross(vTmp, i.vEyeVecTS);
	}
	vNormalTS = normalize(vNormalTS);
	
	i.vEyeVecTS = normalize(i.vEyeVecTS);
	float3 vReflectTS = -reflect(i.vEyeVecTS, vNormalTS);
    

    float4 o = 0;

    for (int iLight = 0; iLight < iNumLights; iLight++)
    {
        float4 vPositionSS = (iLight == 0) ? i.vPositionSS0 : 
                             (iLight == 1) ? i.vPositionSS1 :
                                             i.vPositionSS2;
                                             
    
    	float fShadowMapValue = tex2Dproj(c_sShadowMapSampler, vPositionSS)[iLight];

    	if (fShadowMapValue * vPositionSS.w >= vPositionSS.z)
    	    fShadowMapValue = 1;

    	if (fShadowMapValue >= 1)
    	{
            float3 vLightDirTS = (iLight == 0) ? i.vLightDirTS0.xyz : 
                                 (iLight == 1) ? i.vLightDirTS1.xyz :
                                                 i.vLightDirTS2.xyz;

            //vLightDirTS = normalize(vLightDirTS);
        
        
        	// surface color
        	float3 vDiffuseMaterial = tex2D(g_sDiffuseSampler, i.vTexCoord);
        	float3 vDiffuseColor = max(0, dot(vNormalTS, -vLightDirTS));

        	float3 vSpecularMaterial = tex2D(g_sSpecularSampler, i.vTexCoord);
        	float3 vSpecularColor = pow(max(0, dot(vReflectTS, -vLightDirTS)), g_fSpecularPower);
        	
        	o.rgb += c_axLights[iLight].vLightColor * (vDiffuseMaterial * vDiffuseColor + 
                                                        vSpecularMaterial * vSpecularColor);

        	o.a = tex2D(g_sDiffuseSampler, i.vTexCoord).a;
        }
    }
    
	return o;
}


// AMBIENT -----------------------------------------------------------------------------------------------------------
technique ambientpass_staticmesh
{
	pass p0
	{
		VertexShader = compile vs_1_1 vsMainStatic();
		PixelShader = compile ps_2_0 psMainAmbient();

		AlphaTestEnable = (g_iTransparency);
		AlphaRef = 128;
		AlphaFunc = Greater;
		
		ZEnable = true;
		ZFunc = less;
		ZWriteEnable = true;
	}
}


// LIGHT -------------------------------------------------------------------------------------------------------------
PixelShader psMainLightArray[4] =
{
    compile ps_2_0 psMainLight(0), 
    compile ps_2_0 psMainLight(1), 
    compile ps_2_0 psMainLight(2), 
    compile ps_2_0 psMainLight(3) 
};

technique lightpass_staticmesh
{
	pass p0
	{
		VertexShader = compile vs_1_1 vsMainStatic();
		PixelShader = psMainLightArray[c_iNumLights];

		AlphaTestEnable = (g_iTransparency);
		AlphaRef = 128;
		AlphaFunc = Greater;
	}
}


// SHADOW ------------------------------------------------------------------------------------------------------------
struct VS_OUTPUT_SHADOW
{
	float4 vPositionPS	: POSITION0;
	float4 vPositionPS2	: TEXCOORD0;
	float2 vTexcoord0	: TEXCOORD1_centroid;
};


VS_OUTPUT_SHADOW
vsMainShadowMap(VS_INPUT i)
{
	// preshader->
	float4x4 mWorldView = mul(g_mWorld, g_mView);
	float4x4 mWorldViewProjection = mul(mWorldView, g_mProjection);
	// <-preshader


    i.vPositionOS -= i.vNormalOS * 0.02;


	VS_OUTPUT_SHADOW o;
	o.vPositionPS = mul(float4(i.vPositionOS, 1), mWorldViewProjection);
	o.vPositionPS2 = o.vPositionPS;
	o.vTexcoord0 = i.vTexcoord0;
	return o;
}


float4
psMainShadowMap(VS_OUTPUT_SHADOW i) : COLOR0
{
    float4 vResult; 
    vResult.rgb = i.vPositionPS2.z / i.vPositionPS2.w;
    vResult.a = tex2D(g_sDiffuseSampler, i.vTexcoord0).a; 
    return vResult;    
}


technique shadowmap_staticmesh
{
    pass p0
    {
		VertexShader = compile vs_1_1 vsMainShadowMap();
		PixelShader = compile ps_2_0 psMainShadowMap();

		AlphaTestEnable = (g_iTransparency);
		AlphaRef = 128;
		AlphaFunc = Greater;
    }
}

// -------------------------------------------------------------------------------------------------------------------
