//-----------------------------------------------------------------------------
// Copyright (c) 2008 dhpoware. All Rights Reserved.
//

#define MAX_POINT_LIGHTS 8

struct PointLight
{
	float3 pos;
	float4 ambient;
	float4 diffuse;
	float4 specular;
	float radius;
};

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

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

float4x4 worldMatrix;
float4x4 worldInverseTransposeMatrix;
float4x4 worldViewProjectionMatrix;

float3 cameraPos;
float4 globalAmbient;
int numLights;

PointLight lights[MAX_POINT_LIGHTS];
Material material;

//-----------------------------------------------------------------------------
// 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;
    float4 tangent : TANGENT;
};

struct VS_OUTPUT
{
	float4 position : POSITION;
	float3 worldPos : TEXCOORD0;
	float2 texCoord : TEXCOORD1;
	float3 normal : TEXCOORD2;
	float3 tangent : TEXCOORD3;
	float3 bitangent : TEXCOORD4;
};

VS_OUTPUT VS_PointLighting(VS_INPUT IN)
{
	VS_OUTPUT OUT;

	OUT.position = mul(float4(IN.position, 1.0f), worldViewProjectionMatrix);
	OUT.worldPos = mul(float4(IN.position, 1.0f), worldMatrix).xyz;
	OUT.texCoord = IN.texCoord;
	
	OUT.normal = mul(IN.normal, (float3x3)worldInverseTransposeMatrix);
	OUT.tangent = mul(IN.tangent.xyz, (float3x3)worldInverseTransposeMatrix);
	OUT.bitangent = cross(OUT.normal, OUT.tangent) * IN.tangent.w;
	
	return OUT;
}

//-----------------------------------------------------------------------------
// Pixel Shaders.
//-----------------------------------------------------------------------------

float4 PS_PointLighting(VS_OUTPUT IN) : COLOR
{
    float3 t = normalize(IN.tangent);
    float3 b = normalize(IN.bitangent);
    float3 n = normalize(IN.normal);

    float3x3 tbnMatrix = float3x3(t.x, b.x, n.x,
	                              t.y, b.y, n.y,
	                              t.z, b.z, n.z);
	                                 
    float3 v = normalize(mul(cameraPos - IN.worldPos, tbnMatrix));
    float3 l = float3(0.0f, 0.0f, 0.0f);
    float3 h = float3(0.0f, 0.0f, 0.0f);
    
    float atten = 0.0f;
    float nDotL = 0.0f;
    float nDotH = 0.0f;
    float power = 0.0f;
    
    float4 color = float4(0.0f, 0.0f, 0.0f, 0.0f);
    
    n = normalize(tex2D(normalMap, IN.texCoord).rgb * 2.0f - 1.0f);
    
    for (int i = 0; i < numLights; ++i)
    {
        l = mul((lights[i].pos - IN.worldPos) / lights[i].radius, tbnMatrix);
        atten = saturate(1.0f - dot(l, l));
        
        l = normalize(l);
        h = normalize(l + v);
        
        nDotL = saturate(dot(n, l));
        nDotH = saturate(dot(n, h));
        power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, material.shininess);
        
        color += (material.ambient * (globalAmbient + (atten * lights[i].ambient))) +
                 (material.diffuse * lights[i].diffuse * nDotL * atten) +
                 (material.specular * lights[i].specular * power * atten);
    }
                   
	return color * tex2D(colorMap, IN.texCoord)*2;
}

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

technique NormalMappingPointLighting
{
    pass
    {
        VertexShader = compile vs_3_0 VS_PointLighting();
        PixelShader = compile ps_3_0 PS_PointLighting();
    }
}

