//
// Per pixel dynamic lighting for a single positional light plus light map.
//
// Assumes light map is applied to stage 1.
//
//   Technique t0 - dynamic lighting only
//             t1 - lightmap only
//             t2 - dynamic lighting plus light map
//
// Author: Green Gandalf.
// Created: 31 March 2016.

float4x4 mWVP     : WorldViewProjection;
float4x4 mWorld   : World;
float3 eyePosition : CameraPosition;

float4 lightPosition   = {500.0, 500.0, -500.0, 0.0001}; // w component contains reciprocal of light range
//float4 ambientColour  = {0.0, 0.4, 0.0, 1.0}; // dark green ambient light (not used - should be in light map)
float4 lightColour = {0.0, 0.0, 1.0, 1.0}; // blue positional light
float specularLevel  = 1.0;
float specularExponent = 20.0;

texture baseTexture < string ResourceName = ""; >;

sampler baseSample = sampler_state 
{ texture = <baseTexture>;
  mipFilter = linear;
  magFilter = linear;
  minFilter = linear;
  addressU = clamp;
  addressV = clamp;
};

texture lightMapTexture < string ResourceName = ""; >;

sampler lightMapSample = sampler_state 
{ texture = <lightMapTexture>;
  mipFilter = linear;
  magFilter = linear;
  minFilter = linear;
  addressU = clamp;
  addressV = clamp;
};

struct VSInput
{ float4 pos          : position;
  float2 UV0          : texcoord0;
  float2 UV1          : texcoord1;
  float3 normal       : normal;
};

struct VSOutput
{ float4 pos             : position;
  float2 UV0             : texcoord0;
  float2 UV1             : texcoord1;
  float3 lightDirection  : texcoord2;
  float3 viewDirection   : texcoord3;
  float3 normal          : texcoord4;
};

VSOutput VShader(VSInput In, VSOutput Out)
{ Out.pos = mul(In.pos, mWVP);
  Out.UV0 = In.UV0;
  Out.UV1 = In.UV1;
  float3 wPos = mul(In.pos, mWorld).xyz;
  Out.lightDirection = (wPos - lightPosition.xyz) * lightPosition.w;
  Out.viewDirection = wPos - eyePosition;
  Out.normal = mul(In.normal, (float3x3) mWorld);
  return Out;
}

struct PSOutput { float4 colour : color; };

PSOutput PShader0(VSOutput In, PSOutput Out) // dynamic diffuse and specular lighting only
{ float4 baseColour = tex2D(baseSample, In.UV0);
  float3 normal     = normalize(In.normal);
  float3 lightDirection = normalize(In.lightDirection);
  float diffuse       = saturate(dot(normal, -lightDirection));
  float attenuation = saturate(1.0 - length(In.lightDirection));
  float3 viewDirection = normalize(In.viewDirection);
  float3 reflectDir      = reflect(lightDirection, normal);
  float specular = specularLevel * diffuse * pow(saturate(dot(reflectDir, -viewDirection)), specularExponent);
  Out.colour = baseColour * (diffuse * attenuation * lightColour) + specular * attenuation  * lightColour;
  return Out;
}

PSOutput PShader1(VSOutput In, PSOutput Out) // light map only
{ float4 baseColour = tex2D(baseSample, In.UV0);
  float4 lightMapColour = tex2D(lightMapSample, In.UV1);
  Out.colour = baseColour * lightMapColour;
  return Out;
}

PSOutput PShader2(VSOutput In, PSOutput Out) // combined  dynamic diffuse and specular lighting with light map
{ float4 baseColour = tex2D(baseSample, In.UV0);
  float4 lightMapColour = tex2D(lightMapSample, In.UV1);
  float3 normal     = normalize(In.normal);
  float3 lightDirection = normalize(In.lightDirection);
  float diffuse       = saturate(dot(normal, -lightDirection));
  float attenuation = saturate(1.0 - length(In.lightDirection));
  float3 viewDirection = normalize(In.viewDirection);
  float3 reflectDir      = reflect(lightDirection, normal);
  float specular = specularLevel * diffuse * pow(saturate(dot(reflectDir, -viewDirection)), specularExponent);
  Out.colour = baseColour * (lightMapColour + diffuse * attenuation * lightColour) + specular * attenuation  * lightColour;
  return Out;
}

technique t0
{ pass p0
  { VertexShader = compile vs_2_0 VShader();
    PixelShader = compile ps_2_0 PShader0();
  }
}

technique t1
{ pass p0
  { VertexShader = compile vs_2_0 VShader();
    PixelShader = compile ps_2_0 PShader1();
  }
}

technique t2
{ pass p0
  { VertexShader = compile vs_2_0 VShader();
    PixelShader = compile ps_2_0 PShader2();
  }
}
