//--------------------------------------------------------------------------------------
// File: BasicHLSL.fx
//
// The effect file for the BasicHLSL sample.  
// 
// Copyright (c) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------

#include "GlobalDefines.txt"
#include "Shadows.txt"

//--------------------------------------------------------------------------------------
// Global variables
//--------------------------------------------------------------------------------------
float3 g_LightPos;			        // Light's position in world space
float3 g_LightDir;
float4 g_LightDiffuse;				// Light's diffuse color
float3 g_LightAttenuation;

float3 g_CamPos;					// Camera position

texture g_DiffuseMap;              // Diffuse texture
texture g_HeightMap;				// Height-map
texture g_NormalMap;				// Normal-map

float4x4 g_mWorld;                  // World matrix for object
float4x4 g_mWorldViewProjection;    // World * View * Projection matrix

float4x4 g_mWorldToLightProj;		// Matrix to transform from world space to lights projection space

//--------------------------------------------------------------------------------------
// Texture samplers
//--------------------------------------------------------------------------------------
sampler DiffuseMapSampler = 
sampler_state
{
    Texture = <g_DiffuseMap>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};

sampler HeightMapSampler = 
sampler_state
{
    Texture = <g_HeightMap>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};

sampler NormalMapSampler = 
sampler_state
{
    Texture = <g_NormalMap>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};

//--------------------------------------------------------------------------------------
// Vertex shader output structure
//--------------------------------------------------------------------------------------
struct VS_OUTPUT
{
    float4 Position		: POSITION;		// vertex position
    float2 TextureUV	: TEXCOORD0;	// vertex texture coords 
    float3 View			: TEXCOORD1;	// Un-normalised view vector in tangent space
    float3 Light		: TEXCOORD2;	// Un-normalised light vector in tangent space
    float3 LightWorld	: TEXCOORD3;	// Un-normalised light vector in world space
    float4 PosLight		: TEXCOORD4;	// Fragment position in light's texture space
    float1 Depth		: TEXCOORD5;	// Light-space depth
};

VS_OUTPUT RenderSceneVSMedium(	float4 vPos : POSITION, 
							    float3 vNormal : NORMAL,
								float3 vTangent : TANGENT0,
								float2 vTexCoord0 : TEXCOORD0 )
{
    VS_OUTPUT Output;

    // Transform the position from object space to homogeneous projection space
    Output.Position = mul(vPos, g_mWorldViewProjection);
    
    // Calculate world position
    float3 vWorldPos = mul(vPos, g_mWorld); // position (world space)   
  
    float3 vWorldNormal = mul(vNormal, (float3x3)g_mWorld); // (model space)
    float3 vWorldTangent = mul(vTangent, (float3x3)g_mWorld); // (model space)
    float3 vWorldBiNorm = cross( vTangent, vNormal ); // (model space)
    
	// Copy texture coordinate through
	Output.TextureUV = vTexCoord0; 

	// Calculate light vector
	Output.LightWorld = g_LightPos - vWorldPos; //(world space)
		
	// Move light vector to tangent space
    Output.Light = float3( dot( Output.LightWorld, vWorldTangent ), dot( Output.LightWorld, vWorldBiNorm ), dot( Output.LightWorld, vWorldNormal ) );
  
  	// Calculate view vector
	float3 vView = (g_CamPos - vWorldPos);
		
	// Transform view vector to tangent space
	Output.View = float3( dot( vView, vWorldTangent ), dot( vView, vWorldBiNorm ), dot( vView, vWorldNormal ) );
		
	// Transform fragment position to lights projection space
	Output.PosLight = mul( vPos, g_mWorldToLightProj );
		
	
	Output.Depth = (Output.PosLight.z + SHADOW_NEAR_PLANE) / (SHADOW_FAR_PLANE - SHADOW_NEAR_PLANE);
  	
		
    return Output;    
}


VS_OUTPUT RenderSceneVSLow(	float4 vPos : POSITION, 
							float3 vNormal : NORMAL,
							float3 vTangent : TANGENT0,
							float2 vTexCoord0 : TEXCOORD0 )
{
    VS_OUTPUT Output;

    // Transform the position from object space to homogeneous projection space
    Output.Position = mul(vPos, g_mWorldViewProjection);
    
    // Calculate world position
    float3 vWorldPos = mul(vPos, g_mWorld); // position (world space)   
  
    float3 vWorldNormal = mul(vNormal, (float3x3)g_mWorld); // (model space)
    float3 vWorldTangent = mul(vTangent, (float3x3)g_mWorld); // (model space)
    float3 vWorldBiNorm = cross( vTangent, vNormal ); // (model space)
    
	// Copy texture coordinate through
	Output.TextureUV = vTexCoord0; 

	// Calculate light vector
	Output.LightWorld = g_LightPos - vWorldPos; //(world space)
	
	Output.LightWorld = normalize( Output.LightWorld );
				
	// Move light vector to tangent space
    Output.Light = float3( dot( Output.LightWorld, vWorldTangent ), dot( Output.LightWorld, vWorldBiNorm ), dot( Output.LightWorld, vWorldNormal ) );
  	
  	// Calculate view vector
	float3 vView = (g_CamPos - vWorldPos);
		
	// Transform view vector to tangent space
	Output.View = float3( dot( vView, vWorldTangent ), dot( vView, vWorldBiNorm ), dot( vView, vWorldNormal ) );
		
	Output.View = normalize( Output.View );
	
	// Transform fragment position to lights projection space
	Output.PosLight = mul( vPos, g_mWorldToLightProj );
		
	Output.Depth = (Output.PosLight.z + SHADOW_NEAR_PLANE) / (SHADOW_FAR_PLANE - SHADOW_NEAR_PLANE);

    return Output;    
}


//--------------------------------------------------------------------------------------
// Pixel shader output structure
//--------------------------------------------------------------------------------------
struct PS_OUTPUT
{
    float4 RGBColor : COLOR0;  // Pixel color    
};

PS_OUTPUT RenderScenePSMedium( VS_OUTPUT In, uniform bool bShadow, uniform bool bPCF ) 
{ 
    PS_OUTPUT Output;
	
	float3 vLWorld = normalize( In.LightWorld );
		
	float fDot = dot( vLWorld, g_LightDir );

	float fSpotMul = (fDot > 0.5) ? 1 : 0;
	
	float3 vDiffuse = float3(fSpotMul,fSpotMul,fSpotMul);
	
	// Calculate distance to light
  	float1 fLightDist = length(In.Light);
	
	vDiffuse *= CalculateShadowColour( In.PosLight, In.Depth.x, bShadow, bPCF );
	           
	// Calculate L (tangent space)
	float3 vL = normalize( In.Light );
	
	// Calculate V (tangent space)
	float3 vView = normalize( In.View );
				
	// Get per-pixel displacement
	float1 height = tex2D(HeightMapSampler, In.TextureUV).r * 0.04 - 0.02;
	
	// Parallax mapping
	float2 NewTexCoord;
	NewTexCoord.x = In.TextureUV.x + (vView.x * height);
	NewTexCoord.y = In.TextureUV.y - (vView.y * height);
		
	// Get per-pixel normal
	float3 vN = tex2D(NormalMapSampler, NewTexCoord) * 2.0 - 1.0;
		
	// N.L
	float1 NdotL = clamp( (dot( vN, vL ) * fSpotMul), 0.0, 1.0 );
	
	// Attenuation
	NdotL = NdotL * clamp(g_LightAttenuation.x - g_LightAttenuation.y*sqrt(fLightDist), 0.0, 1.0);
		
	vDiffuse *= g_LightDiffuse * float3(NdotL,NdotL,NdotL);
	
	vDiffuse += float3(0.35,0.35,0.35);
	
	vDiffuse *= tex2D(DiffuseMapSampler, NewTexCoord);
			
    // Lookup mesh texture and modulate it with diffuse
    Output.RGBColor.rgb = vDiffuse;   
	Output.RGBColor.a = 1; 

    return Output;
}


PS_OUTPUT RenderScenePSLow( VS_OUTPUT In ) 
{ 
    PS_OUTPUT Output;
			
	float fDot = dot( In.LightWorld, g_LightDir );

	float fSpotMul = (fDot > 0.5) ? 1 : 0;
	
	float3 vDiffuse = float3(fSpotMul,fSpotMul,fSpotMul);
	
	// Calculate distance to light
  	float1 fLightDist = dot(In.Light,In.Light);
  				
	// Get per-pixel normal
	float3 vN = tex2D(NormalMapSampler, In.TextureUV) * 2.0 - 1.0;
		
	// N.L
	float1 NdotL = clamp( (dot( vN, In.Light ) * fSpotMul), 0.0, 1.0 );
	
	// Attenuation
	//NdotL = NdotL * clamp(g_LightAttenuation.x - g_LightAttenuation.y*fLightDist, 0.0, 1.0);
		
	vDiffuse *= g_LightDiffuse * float3(NdotL,NdotL,NdotL);
	
	vDiffuse += float3(0.35,0.35,0.35);
	
	vDiffuse *= tex2D(DiffuseMapSampler, In.TextureUV);
			
    // Lookup mesh texture and modulate it with diffuse
    Output.RGBColor.rgb = vDiffuse;   
	Output.RGBColor.a = 1; 

    return Output;
}

//--------------------------------------------------------------------------------------
// Renders scene to render target
//--------------------------------------------------------------------------------------
technique RenderScene_High
{
    pass P0
    {          
        VertexShader = compile vs_1_1 RenderSceneVSMedium();
        PixelShader  = compile ps_2_0 RenderScenePSMedium( SHADOWS, SHADOWS_PCF );
    }
}

technique RenderScene_Medium
{
    pass P0
    {          
        VertexShader = compile vs_1_1 RenderSceneVSMedium();
        PixelShader  = compile ps_2_0 RenderScenePSMedium( SHADOWS, SHADOWS_PCF );
    }
}

technique RenderScene_Low
{
    pass P0
    {          
        VertexShader = compile vs_1_1 RenderSceneVSLow();
        PixelShader  = compile ps_1_4 RenderScenePSLow();
    }
}

