#define MAX_POINT_LIGHTS 5

struct Light
{
	float3 dir;                     // world space direction
	float3 pos;                     // world space position
	float4 ambient;
	float4 diffuse;
	float4 specular;
	float spotInnerCone;            // spot light inner cone (theta) angle
	float spotOuterCone;            // spot light outer cone (phi) angle
	float radius;                   // applies to point and spot lights only
};

struct Material
{
	float4 ambient;
	float4 diffuse;
	float4 emissive;
	float4 specular;
	float shininess;
};

//-----------------------------------------------------------------------------
// Globals.
//-----------------------------------------------------------------------------

float4x4 worldMatrix;
float4x4 worldInverseTransposeMatrix;
float4x4 worldViewProjectionMatrix;

float3 cameraPos;
float4 globalAmbient;

Light lights[MAX_POINT_LIGHTS];
Material material;



struct SpotLight
{
	float4 ambient;
	float4 diffuse;
	float4 spec;
	float3 posW;
	float3 dirW;  
	float  spotPower;
};

uniform extern float4x4  gLightWVP;
uniform extern SpotLight gLight;
uniform extern texture   gTex;


sampler TexS = sampler_state
{
	Texture = <gTex>;
	MinFilter = Anisotropic;
	MaxAnisotropy = 8;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
	AddressU  = CLAMP; 
   AddressV  = CLAMP;
};

struct OutputVS
{
    float4 posH     : POSITION0;
    float3 posW     : TEXCOORD0;
    float3 normalW  : TEXCOORD1;
    float3 toEyeW   : TEXCOORD2;
    float4 projTex  : TEXCOORD3;
};


//-----------------------------------------------------------------------------
// Textures.
//-----------------------------------------------------------------------------

texture colorMapTexture;
texture normalMapTexture;

sampler2D colorMap = sampler_state
{
	Texture = <colorMapTexture>;
    MagFilter = Linear;
    MinFilter = Anisotropic;
    MipFilter = Linear;
    MaxAnisotropy = 16;
};

sampler2D normalMap = sampler_state
{
    Texture = <normalMapTexture>;
    MagFilter = Linear;
    MinFilter = Anisotropic;
    MipFilter = Linear;
    MaxAnisotropy = 16;
};

//-----------------------------------------------------------------------------
// Vertex Shaders.
//-----------------------------------------------------------------------------

struct VS_INPUT
{
	float3 position : POSITION;
	float2 texCoord : TEXCOORD0;
	float3 normal : NORMAL;
};

struct VS_NINPUT
{
	float3 position : POSITION;
	float2 texCoord : TEXCOORD0;
	float3 normal : NORMAL;
   float4 tangent : TANGENT;
};

struct VS_OUTPUT_POINT
{
	float4 position : POSITION;
	float2 texCoord : TEXCOORD0;
	float3 viewDir : TEXCOORD1;
	float3 lightDir : TEXCOORD2;
	float3 normal : TEXCOORD3;
	float4 diffuse : COLOR0;
	float4 specular : COLOR1;
};

struct VS_NOUTPUT_POINT
{
	float4 position : POSITION;
	float2 texCoord : TEXCOORD0;
	float3 viewDir : TEXCOORD1;
	float3 lightDir : TEXCOORD2;
	float4 diffuse : COLOR0;
	float4 specular : COLOR1;
};


//-----------------------------------------------------------------------------
// Point Lighting
//-----------------------------------------------------------------------------


VS_OUTPUT_POINT VS_PointLighting(VS_INPUT IN)
{
	VS_OUTPUT_POINT OUT;

	float3 worldPos = mul(float4(IN.position, 1.0f), worldMatrix).xyz;
    
	OUT.position = mul(float4(IN.position, 1.0f), worldViewProjectionMatrix);
	OUT.texCoord = IN.texCoord;
	OUT.viewDir = cameraPos - worldPos;
	OUT.lightDir = (lights[0].pos - worldPos) / lights[0].radius;
	OUT.normal = mul(IN.normal, (float3x3)worldInverseTransposeMatrix);
	OUT.diffuse = material.diffuse * lights[0].diffuse * lights[0].diffuse[3];
	OUT.specular = material.specular * lights[0].specular;
	
	return OUT;
}

VS_OUTPUT_POINT VS_MultiPassPointLighting(VS_INPUT IN, uniform int i)
{
	VS_OUTPUT_POINT OUT;

	float3 worldPos = mul(float4(IN.position, 1.0f), worldMatrix).xyz;
    
	OUT.position = mul(float4(IN.position, 1.0f), worldViewProjectionMatrix);
	OUT.texCoord = IN.texCoord;
	OUT.viewDir = cameraPos - worldPos;
	OUT.lightDir = (lights[i].pos - worldPos) / lights[i].radius;
	OUT.normal = mul(IN.normal, (float3x3)worldInverseTransposeMatrix);
	OUT.diffuse = material.diffuse * lights[i].diffuse * lights[i].diffuse[3];
	OUT.specular = material.specular * lights[i].specular * lights[i].specular[3];
	
	return OUT;
}


float4 PS_PointLighting(VS_OUTPUT_POINT IN) : COLOR
{
    float atten = saturate(1.0f - dot(IN.lightDir, IN.lightDir));
    float3 n = normalize(IN.normal);
    float3 l = normalize(IN.lightDir);
    float3 v = normalize(IN.viewDir);
    float3 h = normalize(l + v);
    
    float nDotL = saturate(dot(n, l));
    float nDotH = saturate(dot(n, h));
    float power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, material.shininess);
    
	float4 color = material.emissive + (material.ambient * (globalAmbient + (atten * lights[0].ambient * lights[0].ambient[3]))) +
                   (IN.diffuse * nDotL * atten) + (IN.specular * power * atten);
                   
	float alpha = tex2D(colorMap, IN.texCoord).w;
	color *= tex2D(colorMap, IN.texCoord);
	color.w = alpha*material.diffuse[3];
	return color;
}

float4 PS_MultiPassPointLighting(VS_OUTPUT_POINT IN, uniform int i) : COLOR
{
    float atten = saturate(1.0f - dot(IN.lightDir, IN.lightDir));
    float3 n = normalize(IN.normal);
    float3 l = normalize(IN.lightDir);
    float3 v = normalize(IN.viewDir);
    float3 h = normalize(l + v);
    
    float nDotL = saturate(dot(n, l));
    float nDotH = saturate(dot(n, h));
    float power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, material.shininess);
    
	float4 color = material.emissive + (material.ambient * atten * lights[i].ambient * lights[i].ambient[3]) +
                   (IN.diffuse * nDotL * atten) + (IN.specular * power * atten);
                   
	float alpha = tex2D(colorMap, IN.texCoord).w;
	color *= tex2D(colorMap, IN.texCoord);
	color.w = alpha*material.diffuse[3];
	return color;
}


//-----------------------------------------------------------------------------
// Normal Mapping.
//-----------------------------------------------------------------------------

VS_NOUTPUT_POINT VS_NMultiPassPointLighting(VS_NINPUT IN, uniform int i)
{
	VS_NOUTPUT_POINT OUT;

	float3 worldPos = mul(float4(IN.position, 1.0f), worldMatrix).xyz;
	float3 viewDir = cameraPos - worldPos;
	float3 lightDir = (lights[i].pos - worldPos) / lights[i].radius;
       
   float3 n = mul(IN.normal, (float3x3)worldInverseTransposeMatrix);
	float3 t = mul(IN.tangent.xyz, (float3x3)worldInverseTransposeMatrix);
	float3 b = cross(n, t) * IN.tangent.w;
	float3x3 tbnMatrix = float3x3(t.x, b.x, n.x,
	                              t.y, b.y, n.y,
	                              t.z, b.z, n.z);
			
	OUT.position = mul(float4(IN.position, 1.0f), worldViewProjectionMatrix);
	OUT.texCoord = IN.texCoord;
	OUT.viewDir = mul(viewDir, tbnMatrix);
	OUT.lightDir = mul(lightDir, tbnMatrix);
	OUT.diffuse = material.diffuse * lights[i].diffuse * lights[i].diffuse[3];
	OUT.specular = material.specular * lights[i].specular * lights[i].specular[3];
	
	return OUT;
}


VS_NOUTPUT_POINT VS_NPointLighting(VS_NINPUT IN)
{
	VS_NOUTPUT_POINT OUT;

	float3 worldPos = mul(float4(IN.position, 1.0f), worldMatrix).xyz;
	float3 viewDir = cameraPos - worldPos;
	float3 lightDir = (lights[0].pos - worldPos) / lights[0].radius;
       
   float3 n = mul(IN.normal, (float3x3)worldInverseTransposeMatrix);
	float3 t = mul(IN.tangent.xyz, (float3x3)worldInverseTransposeMatrix);
	float3 b = cross(n, t) * IN.tangent.w;
	float3x3 tbnMatrix = float3x3(t.x, b.x, n.x,
	                              t.y, b.y, n.y,
	                              t.z, b.z, n.z);
			
	OUT.position = mul(float4(IN.position, 1.0f), worldViewProjectionMatrix);
	OUT.texCoord = IN.texCoord;
	OUT.viewDir = mul(viewDir, tbnMatrix);
	OUT.lightDir = mul(lightDir, tbnMatrix);
	OUT.diffuse = material.diffuse * lights[0].diffuse * lights[0].diffuse[3];
	OUT.specular = material.specular * lights[0].specular * lights[0].specular[3];
	
	return OUT;
}

float4 PS_NPointLighting(VS_NOUTPUT_POINT IN) : COLOR
{
    float atten = saturate(1.0f - dot(IN.lightDir, IN.lightDir));

	 float3 n = normalize(tex2D(normalMap, IN.texCoord).rgb * 2.0f - 1.0f);
    float3 l = normalize(IN.lightDir);
    float3 v = normalize(IN.viewDir);
    float3 h = normalize(l + v);
    
    float nDotL = saturate(dot(n, l));
    float nDotH = saturate(dot(n, h));
    float power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, material.shininess);


    float4 color = (material.ambient * (globalAmbient + (atten * lights[0].ambient * lights[0].ambient[3]))) +
                   (IN.diffuse * nDotL * atten) + (IN.specular * power * atten);
                       
	return color * tex2D(colorMap, IN.texCoord);
}

float4 PS_NMultiPassPointLighting(VS_NOUTPUT_POINT IN, uniform int i) : COLOR
{
    float atten = saturate(1.0f - dot(IN.lightDir, IN.lightDir));

	 float3 n = normalize(tex2D(normalMap, IN.texCoord).rgb * 2.0f - 1.0f);
    float3 l = normalize(IN.lightDir);
    float3 v = normalize(IN.viewDir);
    float3 h = normalize(l + v);
    
    float nDotL = saturate(dot(n, l));
    float nDotH = saturate(dot(n, h));
    float power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, material.shininess);


    float4 color = (material.ambient * atten * lights[i].ambient * lights[i].ambient[3]) +
                   (IN.diffuse * nDotL * atten) + (IN.specular * power * atten);
                    
	return color * tex2D(colorMap, IN.texCoord);
}


//-----------------------------------------------------------------------------
// Projected Texture
//-----------------------------------------------------------------------------

OutputVS ProjTexVS(float3 posL : POSITION0, float3 normalL : NORMAL0)
{
    // Zero out our output.
	OutputVS outVS = (OutputVS)0;
	
	// Transform normal to world space.
	outVS.normalW = mul(float4(normalL, 0.0f), worldInverseTransposeMatrix).xyz;
	
	// Transform vertex position to world space.
	outVS.posW  = mul(float4(posL, 1.0f), worldMatrix).xyz;
	
	// Compute the unit vector from the vertex to the eye.
	outVS.toEyeW = cameraPos - outVS.posW;
	
	// Transform to homogeneous clip space.
	outVS.posH = mul(float4(posL, 1.0f), worldViewProjectionMatrix);
	
	// Render from light source to generate projective texture coordinates.
	outVS.projTex = mul(float4(posL, 1.0f), gLightWVP);
		
	// Done--return the output.
    return outVS;
}

float4 ProjTexPS(float3 posW     : TEXCOORD0,
                 float3 normalW  : TEXCOORD1,
                 float3 toEyeW   : TEXCOORD2,
                 float4 projTex  : TEXCOORD3) : COLOR
{
	// Interpolated normals can become unnormal--so normalize.
	normalW = normalize(normalW);
	toEyeW  = normalize(toEyeW);
	
	// Light vector is from pixel to spotlight position.
	float3 lightVecW = normalize(gLight.posW - posW);
	
	// Compute the reflection vector.
	float3 r = reflect(-lightVecW, normalW);
	
	// Determine how much (if any) specular light makes it into the eye.
	float t  = pow(max(dot(r, toEyeW), 0.0f), material.shininess);
	
	// Determine the diffuse light intensity that strikes the vertex.
	float s = max(dot(lightVecW, normalW), 0.0f);
	
	// Compute the ambient, diffuse and specular terms separately. 
	//float4 spec = float4(normalize(t*(material.specular*gLight.spec).rgb),material.specular.a*gLight.spec.a);
	//float4 diffuse = float4(normalize(s*(material.diffuse*gLight.diffuse)).rgb,material.diffuse.a*gLight.diffuse.a);
	//float4 ambient = normalize(material.ambient*gLight.ambient);
	
	
	// Compute spotlight coefficient.
	//float spot = pow(max( dot(-lightVecW, gLight.dirW), 0.0f), gLight.spotPower);
	
	// Project the texture coords and scale/offset to [0, 1].
	//projTex.xy /= projTex.w;            
	//projTex.x =  0.5f*projTex.x + 0.5f; 
	//projTex.y = -0.5f*projTex.y + 0.5f;

	// Sample tex w/ projective texture coords.
	//float4 texColor = tex2D(TexS, projTex.xy); 
	
	// Only project/light in spotlight cone.
	//float4 litColor = float4(spot*(ambient.rgb+diffuse.rgb*texColor.rgb + spec.rgb),diffuse.a*texColor.a);

	// Output the color and the alpha.
   //return litColor; 
   
   
   // Compute the ambient, diffuse and specular terms separately. 
	float4 spec = float4(t*(material.specular*gLight.spec).rgb,material.specular.a*gLight.spec.a);
	float4 diffuse = float4(s*(material.diffuse*gLight.diffuse).rgb,material.diffuse.a*gLight.diffuse.a);
	float4 ambient = material.ambient*gLight.ambient;
		
	// Compute spotlight coefficient.
	float spot = pow(max( dot(-lightVecW, gLight.dirW), 0.0f), gLight.spotPower);
	
	// Project the texture coords and scale/offset to [0, 1].
	projTex.xy /= projTex.w;            
	projTex.x =  0.5f*projTex.x + 0.5f; 
	projTex.y = -0.5f*projTex.y + 0.5f;

	// Sample tex w/ projective texture coords.
	float4 texColor = tex2D(TexS, projTex.xy); 
	
	// Only project/light in spotlight cone.
	//float3 litColor = spot*(diffuse.rgb*texColor.rgb + spec.rgb);
	float3 litColor = spot*(ambient+diffuse*texColor + spec).rgb;
	
	// Output the color and the alpha.
   return float4(normalize(litColor),diffuse.a*texColor.a);
   
}

//-----------------------------------------------------------------------------
// Techniques.
//-----------------------------------------------------------------------------


technique PerPixelPointLighting
{
    pass
    {       
		//AlphaBlendEnable = FALSE;
		VertexShader = compile vs_2_0 VS_PointLighting();
      PixelShader = compile ps_2_0 PS_PointLighting();
    }
}

technique PerPixelPointLightingProjected
{
    pass Light
    {       
		AlphaBlendEnable = FALSE;
		VertexShader = compile vs_2_0 VS_PointLighting();
      PixelShader = compile ps_2_0 PS_PointLighting();
    }
     pass Projected
    {       
		AlphaBlendEnable = TRUE;
      SrcBlend = SRCALPHA;
      DestBlend = ONE;
		VertexShader = compile vs_2_0 ProjTexVS();
      PixelShader  = compile ps_2_0 ProjTexPS();
    }
}


technique PerPixelPointLightingTrans
{
    pass
    {       
		AlphaBlendEnable = TRUE;
    	SrcBlend = SRCALPHA;
    	DestBlend = INVSRCALPHA;
    	//AlphaTestEnable = TRUE;	
		VertexShader = compile vs_2_0 VS_PointLighting();
      PixelShader = compile ps_2_0 PS_PointLighting();
      
    }
}

technique PerPixelPointLightingMultiPass2
{
    pass Light1
    {
        AlphaBlendEnable = FALSE;
        VertexShader = compile vs_2_0 VS_PointLighting();
        PixelShader = compile ps_2_0 PS_PointLighting();
    }   
   
    pass Light2
    {
        AlphaBlendEnable = TRUE;
        SrcBlend = ONE;
        DestBlend = ONE;
    
        VertexShader = compile vs_2_0 VS_MultiPassPointLighting(1);
        PixelShader = compile ps_2_0 PS_MultiPassPointLighting(1);
        
    }

}

technique PerPixelPointLightingMultiPass3
{
    pass Light1
    {
        AlphaBlendEnable = FALSE;
        VertexShader = compile vs_2_0 VS_PointLighting();
        PixelShader = compile ps_2_0 PS_PointLighting();
    }   
   
    pass Light2
    {
        AlphaBlendEnable = TRUE;
        SrcBlend = ONE;
        DestBlend = ONE;
    
        VertexShader = compile vs_2_0 VS_MultiPassPointLighting(1);
        PixelShader = compile ps_2_0 PS_MultiPassPointLighting(1);
    }

    pass Light3
    {
        AlphaBlendEnable = TRUE;
        SrcBlend = ONE;
        DestBlend = ONE;
    
        VertexShader = compile vs_2_0 VS_MultiPassPointLighting(2);
        PixelShader = compile ps_2_0 PS_MultiPassPointLighting(2);
        
    }

}



technique NormalMappingPointLighting
{
    pass
    {
        //AlphaBlendEnable = FALSE;
        VertexShader = compile vs_2_0 VS_NPointLighting();
        PixelShader = compile ps_2_0 PS_NPointLighting();
    }
}


technique NormalMappingPointLightingProjected
{
    pass Light
    {       
		//AlphaBlendEnable = FALSE;
		VertexShader = compile vs_2_0 VS_NPointLighting();
      PixelShader = compile ps_2_0 PS_NPointLighting();
    }
    
     pass Projected
    {       
		AlphaBlendEnable = TRUE;
      SrcBlend = SRCALPHA;
      DestBlend = ONE;
		VertexShader = compile vs_2_0 ProjTexVS();
      PixelShader  = compile ps_2_0 ProjTexPS();
    }
}

technique NormalMappingPointLightingMultiPass2
{
    pass Light1
    {
        AlphaBlendEnable = FALSE;
        VertexShader = compile vs_2_0 VS_NPointLighting();
        PixelShader = compile ps_2_0 PS_NPointLighting();
    }   
   
    pass Light2
    {
        AlphaBlendEnable = TRUE;
        SrcBlend = ONE;
        DestBlend = ONE;
    
        VertexShader = compile vs_2_0 VS_NMultiPassPointLighting(1);
        PixelShader = compile ps_2_0 PS_NMultiPassPointLighting(1);
        
    }


}

technique NormalMappingPointLightingMultiPass3
{
    pass Light1
    {
        AlphaBlendEnable = FALSE;
        VertexShader = compile vs_2_0 VS_NPointLighting();
        PixelShader = compile ps_2_0 PS_NPointLighting();
    }   
   
    pass Light2
    {
        AlphaBlendEnable = TRUE;
        SrcBlend = ONE;
        DestBlend = ONE;
    
        VertexShader = compile vs_2_0 VS_NMultiPassPointLighting(1);
        PixelShader = compile ps_2_0 PS_NMultiPassPointLighting(1);
    }

    pass Light3
    {
        AlphaBlendEnable = TRUE;
        SrcBlend = ONE;
        DestBlend = ONE;
    
        VertexShader = compile vs_2_0 VS_NMultiPassPointLighting(2);
        PixelShader = compile ps_2_0 PS_NMultiPassPointLighting(2);
        
        
    }

}



