/*********************************************************************NVMH3****

Terrain rendering.
This is a port of the old DX8 ASM shaders --
but with a normal map per layer, and support for 3 directional lights and 3 points.
-specular mask per layer
-height map per layer for parallax mapping

******************************************************************************/

float4x4 worldViewProj : WorldViewProjection;
float4x4 viewProj : ViewProjection;
float4x4 world   : World;

#include "..\mrt.fx"

struct DirectionalLight
{
    float3 Direction    : DIRECTION;
    float3 Diffuse      : DIFFUSE;
    float3 Specular     : SPECULAR;
};

struct PointLight
{
    float4 Position_Att0     : POSITION_ATT0;
    float4 Diffuse_Att1      : DIFFUSE_ATT1;
    float4 Specular_Att2     : SPECULAR_ATT2;
};

struct Material
{
    float4 Diffuse_Opacity  : DIFFUSE_OPACITY;
    float4 Specular_Power : SPECULAR_POWER;
    float3 Ambient  : AMBIENT;
    float3 Emissive : EMISSIVE;
};

DirectionalLight    dirLights[3]    : DIRLIGHT;
PointLight          pointLights[3]  : POINTLIGHT;
Material            matSettings   : MATERIAL;

float4 DiffuseOpacity0 : DIFFUSE_OPACITY0;
float4 DiffuseOpacity1 : DIFFUSE_OPACITY1;
float4 DiffuseOpacity2 : DIFFUSE_OPACITY2;
float4 DiffuseOpacity3 : DIFFUSE_OPACITY3;

float4 Specular_Power0 : SPECULAR_POWER0;
float4 Specular_Power1 : SPECULAR_POWER1;
float4 Specular_Power2 : SPECULAR_POWER2;
float4 Specular_Power3 : SPECULAR_POWER3;

float3 Surface_Ambient0 : SURFACE_AMBIENT0;
float3 Surface_Ambient1 : SURFACE_AMBIENT1;
float3 Surface_Ambient2 : SURFACE_AMBIENT2;
float3 Surface_Ambient3 : SURFACE_AMBIENT3;

float3 ambientLevel : AMBIENT = {0.3, 0.3, 0.3};
float3 campos : CAMERAPOS;

float4x4 UVTransform0 : UVTransform4x4;
float4x4 UVTransform1 : UVTransform4x4;
float4x4 UVTransform2 : UVTransform4x4;
float4x4 UVTransform3 : UVTransform4x4;


// Parallax mapping constants
float HeightScale0 : HEIGHTSCALE0 = 0.0;
float HeightScale1 : HEIGHTSCALE1 = 0.0;
float HeightScale2 : HEIGHTSCALE2 = 0.0;
float HeightScale3 : HEIGHTSCALE3 = 0.0;

float HeightBias0 : HEIGHTBIAS0 = 0.0;
float HeightBias1 : HEIGHTBIAS1 = 0.0;
float HeightBias2 : HEIGHTBIAS2 = 0.0;
float HeightBias3 : HEIGHTBIAS3 = 0.0;


// UV rotate bad! 
float4x4 UVRotate : UVRotate4x4;

float  timeScale : TimeScale = 0.0f;

texture revealTexture0 : DiffuseTexture;
sampler RevealTextureSampler0 = sampler_state 
{
    texture = <revealTexture0>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

texture revealTexture1 : Custom0;
sampler RevealTextureSampler1 = sampler_state 
{
    texture = <revealTexture1>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

texture revealTexture2 : Custom1;
sampler RevealTextureSampler2 = sampler_state 
{
    texture = <revealTexture2>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

texture revealTexture3 : Custom2;
sampler RevealTextureSampler3 = sampler_state 
{
    texture = <revealTexture3>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};


texture revealBumpTexture0 : BumpTexture;
sampler RevealBumpTextureSampler0 = sampler_state 
{
    texture = <revealBumpTexture0>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

texture revealBumpTexture1 : Custom3;
sampler RevealBumpTextureSampler1 = sampler_state 
{
    texture = <revealBumpTexture1>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

texture revealBumpTexture2 : Custom4;
sampler RevealBumpTextureSampler2 = sampler_state 
{
    texture = <revealBumpTexture2>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

texture revealBumpTexture3 : Custom5;
sampler RevealBumpTextureSampler3 = sampler_state 
{
    texture = <revealBumpTexture3>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};


//---------------------------
// Specular masks
texture specMaskTexture0 : Custom6;
sampler SpecMaskTextureSampler0 = sampler_state 
{
    texture = <specMaskTexture0>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

texture specMaskTexture1 : Custom7;
sampler SpecMaskTextureSampler1 = sampler_state 
{
    texture = <specMaskTexture1>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

texture specMaskTexture2 : Custom8;
sampler SpecMaskTextureSampler2 = sampler_state 
{
    texture = <specMaskTexture2>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

texture specMaskTexture3 : Custom9;
sampler SpecMaskTextureSampler3 = sampler_state 
{
    texture = <specMaskTexture3>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};


//---------------------------
// Heightmaps for parallax mapping
texture heightTexture0 : Custom10;
sampler HeightTextureSampler0 = sampler_state 
{
    texture = <heightTexture0>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

texture heightTexture1 : Custom11;
sampler HeightTextureSampler1 = sampler_state 
{
    texture = <heightTexture1>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

texture heightTexture2 : Custom12;
sampler HeightTextureSampler2 = sampler_state 
{
    texture = <heightTexture2>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

texture heightTexture3 : Custom13;
sampler HeightTextureSampler3 = sampler_state 
{
    texture = <heightTexture3>;
    AddressU  = WRAP;        
    AddressV  = WRAP;
    AddressW  = WRAP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};



//------------------------------------
struct vertexInput {
    float4 position				: POSITION;
    float4 blendgoo				: COLOR0;
    float3 normal               : NORMAL;
    float4 tangentVec			: TANGENT;
    float2 noisefactor			: COLOR1;
};    

struct vertexOutput
{
	float4 hPosition : POSITION;
    float4 blendgoo         : COLOR0;
  //  float2 texCoordDiffuse0	: TEXCOORD0;
  //  float2 texCoordDiffuse1	: TEXCOORD1;
  //  float2 texCoordDiffuse2	: TEXCOORD2;
  //  float2 texCoordDiffuse3	: TEXCOORD3;
    float3 posWorld         : TEXCOORD0;
    float3 lightVec	        : TEXCOORD1;
    float3 lightVec2        : TEXCOORD2;
    float3 lightVec3        : TEXCOORD3;
    float3 viewVec 	        : TEXCOORD4;
    float3 pointLight		: TEXCOORD5;
    float3 pointLight2		: TEXCOORD6;
    float3 pointLight3		: TEXCOORD7;
};


float WorkingSaturate(float x)
{
    if (x<0)
        x = 0;
    else if (x>1)
        x = 1;
    return x;
}



//------------------------------------
vertexOutput VS_TransformAndTexture(vertexInput IN) 
{
    vertexOutput OUT;
    
    float4 newPos = IN.position;
    //newPos.y += sin(timeScale) * 7.0f * IN.noisefactor.x;
    
    OUT.hPosition = mul( newPos, worldViewProj);
        
    OUT.blendgoo = IN.blendgoo;
  
	// multiply tangents by rotation    
    float4 newTangent = mul( IN.tangentVec, UVRotate );
    newTangent = normalize(newTangent);
	
	float3x3 worldToTangentSpace;
	worldToTangentSpace[0] = normalize( mul( newTangent.xyz, world ) );
	worldToTangentSpace[1] = normalize( mul( cross(newTangent.xyz, IN.normal), world)*newTangent.w );	
	worldToTangentSpace[2] = normalize( mul( IN.normal, world ) );

    float4 pos;
    float3 norm;
    float3 tang;
    
    OUT.posWorld = mul(newPos, world);

	// -direction
	OUT.lightVec = mul( worldToTangentSpace, -dirLights[0].Direction );
	OUT.lightVec2 = mul( worldToTangentSpace, -dirLights[1].Direction );
	OUT.lightVec3 = mul( worldToTangentSpace, -dirLights[2].Direction );

	OUT.viewVec = mul( worldToTangentSpace, campos - OUT.posWorld );
	
	// point light: -direction
	float3 pLightDir = pointLights[0].Position_Att0.xyz - OUT.posWorld.xyz;
	OUT.pointLight.xyz = ( mul( worldToTangentSpace, (pLightDir) ) );
	
	float3 pLightDir2 = pointLights[1].Position_Att0.xyz - OUT.posWorld.xyz;
	OUT.pointLight2.xyz = ( mul( worldToTangentSpace, (pLightDir2) ) );

	float3 pLightDir3 = pointLights[2].Position_Att0.xyz - OUT.posWorld.xyz;
	OUT.pointLight3.xyz = ( mul( worldToTangentSpace, (pLightDir3) ) );
		
    return OUT;
}

// Calculates diffuse and specular lighting for a point light, including light colors
void PS_PointLightDS(PointLight pointL, float3 lightToPixelT, float3 surfaceNormal, float3 posWorld, out float3 pointDiffuse, float power, float3 eyeVecT, out float3 pointSpec)
{
	float3 lightToPixelW = pointL.Position_Att0.xyz - posWorld;	
	float distSquared = dot( lightToPixelW, lightToPixelW );
	float dist = sqrt( distSquared );
	// attenuation
	// att = 1 / (a0 + a1*d + a2*d*d)
	float atten = 1 / (pointL.Position_Att0.w + pointL.Diffuse_Att1.w*dist + pointL.Specular_Att2.w*distSquared );		
	float3 pointLightDir = normalize( lightToPixelT );
	float LdN = dot(surfaceNormal, pointLightDir);
	
	float3 halfVec = normalize(pointLightDir + eyeVecT);
    float ndH = dot(halfVec, surfaceNormal);
    
    float4 vLit = lit(LdN, ndH, power);
    pointDiffuse = vLit.y*atten*pointL.Diffuse_Att1.xyz;	
	pointSpec = vLit.z *atten*pointL.Specular_Att2.xyz;
}


// Calculates diffuse and specular lighting for a directional light, including the light colors
void PS_DirLightDS(DirectionalLight dirLight, float3 dirLightDirT, float3 pixelNormalT, out float3 diffuse, float power, float3 eyeVecT, out float3 specular)
{
    float LdN = dot(-dirLightDirT, pixelNormalT);
    float3 halfVec = normalize((-dirLightDirT + eyeVecT));
    float ndH = dot(halfVec, pixelNormalT);
    float4 vLit = lit(LdN, ndH, power);
    diffuse = dirLight.Diffuse * vLit.y;
    specular = vLit.z * dirLight.Specular;
}


//-----------------------------------
PS_OUTPUT PS_Textured( vertexOutput IN)
{ 		
	float2 texCoordDiffuse0;
	float2 texCoordDiffuse1;
	float2 texCoordDiffuse2;
	float2 texCoordDiffuse3;
	
	float u = IN.posWorld.x;
    float v = -IN.posWorld.z;
   
   	float4 baseUVs = float4(u,0,v,1); 	
	float3 viewDir = normalize( IN.viewVec );
		
   	float4 uvs = mul( baseUVs, UVTransform0 );     
    texCoordDiffuse0.x = uvs.x;
    texCoordDiffuse0.y = uvs.z;
    
    uvs = mul( baseUVs, UVTransform1 );
    texCoordDiffuse1.x = uvs.x;
    texCoordDiffuse1.y = uvs.z;

    uvs = mul( baseUVs, UVTransform2 );
    texCoordDiffuse2.x = uvs.x;
    texCoordDiffuse2.y = uvs.z;

    uvs = mul( baseUVs, UVTransform3 );
    texCoordDiffuse3.x = uvs.x;
    texCoordDiffuse3.y = uvs.z;
    
    // Parallax mapping: offset uvs based on height
    float4 height0 = tex2D( HeightTextureSampler0, texCoordDiffuse0 );
	float4 height1 = tex2D( HeightTextureSampler1, texCoordDiffuse1 );
	float4 height2 = tex2D( HeightTextureSampler2, texCoordDiffuse2 );
	float4 height3 = tex2D( HeightTextureSampler3, texCoordDiffuse3 );
	
	//return float4( height0.x, height0.x, height0.x, 1 );
	
	height0.x = HeightScale0 * height0.x + HeightBias0;
	height1.x = HeightScale1 * height1.x + HeightBias1;
	height2.x = HeightScale2 * height2.x + HeightBias2;
	height3.x = HeightScale3 * height3.x + HeightBias3;
	
	texCoordDiffuse0 = (height0.x * viewDir) + texCoordDiffuse0;
	texCoordDiffuse1 = (height1.x * viewDir) + texCoordDiffuse1;
	texCoordDiffuse2 = (height2.x * viewDir) + texCoordDiffuse2;
	texCoordDiffuse3 = (height3.x * viewDir) + texCoordDiffuse3;
    
 	float4 diffuseTexture = tex2D( RevealTextureSampler0, texCoordDiffuse0 );
    float3 normalTexture = (2 * (tex2D( RevealBumpTextureSampler0, texCoordDiffuse0 ))) - 1;

	float4 revealTexture0 = tex2D( RevealTextureSampler1, texCoordDiffuse1 );
  	float4 revealTexture1 = tex2D( RevealTextureSampler2, texCoordDiffuse2 );
  	float4 revealTexture2 = tex2D( RevealTextureSampler3, texCoordDiffuse3 );

    float3 revealNormalTexture0 = (2 * (tex2D( RevealBumpTextureSampler1, texCoordDiffuse1 ))) - 1;
    float3 revealNormalTexture1 = (2 * (tex2D( RevealBumpTextureSampler2, texCoordDiffuse2 ))) - 1;
    float3 revealNormalTexture2 = (2 * (tex2D( RevealBumpTextureSampler3, texCoordDiffuse3 ))) - 1;
   
	// Specular masks
	float4 specMask0 = tex2D( SpecMaskTextureSampler0, texCoordDiffuse0 );
	float4 specMask1 = tex2D( SpecMaskTextureSampler1, texCoordDiffuse1 );
	float4 specMask2 = tex2D( SpecMaskTextureSampler2, texCoordDiffuse2 );
	float4 specMask3 = tex2D( SpecMaskTextureSampler3, texCoordDiffuse3 );
       
  	float4 mixedDiffuse = lerp( diffuseTexture, revealTexture0, IN.blendgoo.x );
  	mixedDiffuse = lerp( mixedDiffuse, revealTexture1, IN.blendgoo.y );
  	mixedDiffuse = lerp( mixedDiffuse, revealTexture2, IN.blendgoo.z );
  
   	float3 mixedNormal = lerp( normalTexture, revealNormalTexture0, IN.blendgoo.x );
  	mixedNormal = lerp( mixedNormal, revealNormalTexture1, IN.blendgoo.y );
  	mixedNormal = lerp( mixedNormal, revealNormalTexture2, IN.blendgoo.z );
	mixedNormal = normalize(mixedNormal);
	
	float3 mixedSpecularMask = lerp( specMask0, specMask1, IN.blendgoo.x );
  	mixedSpecularMask = lerp( mixedSpecularMask, specMask2, IN.blendgoo.y );
  	mixedSpecularMask = lerp( mixedSpecularMask, specMask3, IN.blendgoo.z );
	mixedSpecularMask = normalize(mixedSpecularMask);

	float  mixedSpecPower = lerp( Specular_Power0.w, Specular_Power1.w, IN.blendgoo.x );
	mixedSpecPower = lerp( mixedSpecPower, Specular_Power2.w, IN.blendgoo.y );
	mixedSpecPower = lerp( mixedSpecPower, Specular_Power3.w, IN.blendgoo.z );
	
	float3 mixedSpecColor = lerp( Specular_Power0.xyz, Specular_Power1.xyz, IN.blendgoo.x );
	mixedSpecColor = lerp( mixedSpecColor, Specular_Power2.xyz, IN.blendgoo.y );
	mixedSpecColor = lerp( mixedSpecColor, Specular_Power3.xyz, IN.blendgoo.z );
	
	float4 mixedMatDiffuse = lerp( DiffuseOpacity0, DiffuseOpacity1, IN.blendgoo.x );
	mixedMatDiffuse = lerp( mixedMatDiffuse, DiffuseOpacity2, IN.blendgoo.y );
	mixedMatDiffuse = lerp( mixedMatDiffuse, DiffuseOpacity3, IN.blendgoo.z );

	float3 mixedMatAmbient = lerp( Surface_Ambient0, Surface_Ambient1, IN.blendgoo.x );
	mixedMatAmbient = lerp( mixedMatAmbient, Surface_Ambient2, IN.blendgoo.y );
	mixedMatAmbient = lerp( mixedMatAmbient, Surface_Ambient3, IN.blendgoo.z );
	
	float3 lightDir = normalize( IN.lightVec );
	float3 lightDir2 = normalize( IN.lightVec2 );
	float3 lightDir3 = normalize( IN.lightVec3 );
	
	
	float3 dirDiffuse;
	float3 dirSpec;
	PS_DirLightDS( dirLights[0], -lightDir, mixedNormal, dirDiffuse, mixedSpecPower, viewDir, dirSpec );
	
	float3 dirDiffuse2;
	float3 dirSpec2;
	PS_DirLightDS( dirLights[1], -lightDir2, mixedNormal, dirDiffuse2, mixedSpecPower, viewDir, dirSpec2 );
	
	float3 dirDiffuse3;
	float3 dirSpec3;
	PS_DirLightDS( dirLights[2], -lightDir3, mixedNormal, dirDiffuse3, mixedSpecPower, viewDir, dirSpec3 );
	
	// 1st point light
	float3 pointDiffuse;
	float3 pointSpec;
	PS_PointLightDS(pointLights[0], IN.pointLight.xyz, mixedNormal.xyz, IN.posWorld, pointDiffuse, mixedSpecPower, viewDir, pointSpec);

	// 2nd point light: 
	float3 pointDiffuse2;
	float3 pointSpec2;
	PS_PointLightDS(pointLights[1], IN.pointLight2.xyz, mixedNormal.xyz, IN.posWorld, pointDiffuse2, mixedSpecPower, viewDir, pointSpec2);
	
	// 3rd point light: 
	float3 pointDiffuse3;
	float3 pointSpec3;
	PS_PointLightDS(pointLights[2], IN.pointLight3.xyz, mixedNormal.xyz, IN.posWorld, pointDiffuse3, mixedSpecPower, viewDir, pointSpec3);
		
	float3 pointLightSum;
	pointLightSum = pointDiffuse;
	pointLightSum += pointDiffuse2;
	pointLightSum += pointDiffuse3;
	
	float3 directionalSum;
	directionalSum = dirDiffuse*dirLights[0].Diffuse;
	directionalSum += dirDiffuse2*dirLights[1].Diffuse;
	directionalSum += dirDiffuse3*dirLights[2].Diffuse;
	
	float3 specularSum;
	specularSum = (pointSpec + pointSpec2 + pointSpec3);
	specularSum += (dirSpec + dirSpec2 + dirSpec3); 
	
	float4 outColor = float4(0,0,0,1);// = mixedMatDiffuse * mixedDiffuse;
	outColor.xyz  = (directionalSum + pointLightSum);
	outColor.xyz += (specularSum * mixedSpecColor * mixedSpecularMask);
    outColor.xyz += mixedMatAmbient * ambientLevel;

	PS_OUTPUT Out;  
    //SetPSOutput( Out, outColor, float4( campos - IN.posWorld.xyz, 1) );
    SetPSOutput( Out, outColor, float4( campos - IN.posWorld.xyz, 1) );
    return Out;
}


//-----------------------------------
technique textured
{
    pass p0 
    {		        
		VertexShader = compile vs_3_0 VS_TransformAndTexture();
		PixelShader  = compile ps_3_0 PS_Textured();
    }
}
