////////////////////////////////////////////////////////////////////////////////////////////////////
// land.fx
////////////////////////////////////////////////////////////////////////////////////////////////////



// T R A N S F O R M S
////////////////////////////////////////////////////////////////////////////////////////////////////
float4x4 g_wvp_mtx;
float4x4 g_world_mtx;
float4x4 g_shadow_mtx;
float4 g_sun_dir;
float4 g_cam_pos;
float4 g_land_scale;



// T E X T U R E S
////////////////////////////////////////////////////////////////////////////////////////////////////
texture2D g_grass_tex;
texture2D g_dirt_tex;
texture2D g_rock_tex;
texture2D g_normal_tex;
texture2D g_shadowmap_tex;
textureCUBE g_env_tex;
texture2D g_noise_tex;
texture2D g_shadowtex_tex;



// S A M P L E R S
////////////////////////////////////////////////////////////////////////////////////////////////////
sampler2D g_grass_samp = sampler_state 
{
		texture = <g_grass_tex>;
		MaxAnisotropy = 8;
		MinFilter = ANISOTROPIC;
		MagFilter = LINEAR;
		MipFilter = LINEAR;
		AddressU  = WRAP;        
		AddressV  = WRAP;
		AddressW  = WRAP;
};

sampler2D g_dirt_samp = sampler_state 
{
		texture = <g_dirt_tex>;
		MaxAnisotropy = 8;
		MinFilter = ANISOTROPIC;
		MagFilter = LINEAR;
		MipFilter = LINEAR;
		AddressU  = WRAP;        
		AddressV  = WRAP;
		AddressW  = WRAP;
};

sampler2D g_rock_samp = sampler_state 
{
		texture = <g_rock_tex>;
		MaxAnisotropy = 8;
		MinFilter = ANISOTROPIC;
		MagFilter = LINEAR;
		MipFilter = LINEAR;
		AddressU  = WRAP;        
		AddressV  = WRAP;
		AddressW  = WRAP;
};

sampler2D g_normal_samp = sampler_state 
{
		texture = <g_normal_tex>;
		MaxAnisotropy = 16;
		MinFilter = ANISOTROPIC;
		MagFilter = LINEAR;
		MipFilter = LINEAR;
		AddressU  = WRAP;        
		AddressV  = WRAP;
		AddressW  = WRAP;
};

sampler2D g_shadowmap_samp = sampler_state 
{
		texture = <g_shadowmap_tex>;
		MinFilter = POINT;
		MagFilter = POINT;
		MipFilter = NONE;
		AddressU  = CLAMP;        
		AddressV  = CLAMP;
		AddressW  = CLAMP;
};

samplerCUBE g_env_samp = sampler_state 
{
		texture = <g_env_tex>;
		MinFilter = LINEAR;
		MagFilter = LINEAR;
		MipFilter = LINEAR;
		AddressU  = CLAMP;        
		AddressV  = CLAMP;
		AddressW  = CLAMP;
};

sampler2D g_noise_samp = sampler_state 
{
		texture = <g_noise_tex>;
		MinFilter = LINEAR;
		MagFilter = LINEAR;
		MipFilter = LINEAR;
		AddressU  = WRAP;        
		AddressV  = WRAP;
		AddressW  = WRAP;
};

sampler2D g_shadowtex_samp = sampler_state 
{
		texture = <g_shadowtex_tex>;
		MinFilter = LINEAR;
		MagFilter = LINEAR;
		MipFilter = LINEAR;
		AddressU  = WRAP;        
		AddressV  = WRAP;
		AddressW  = WRAP;
};



// Vertex shaders
////////////////////////////////////////////////////////////////////////////////////////////////////
void vs_zpass(
    in float3 iPos : POSITION0,
    out float4 oPos	: POSITION )
{
		// Transform vertex position to clip space
		float4 scaled_pos = float4( iPos.xyz * g_land_scale.xyz, 1.0 );
		oPos = mul( scaled_pos, g_wvp_mtx );	
}



////////////////////////////////////////////////////////////////////////////////////////////////////
void vs_main(
    in float3 iPos : POSITION0,

    out float4 oPos	: POSITION,
    out float4 oTc0	: TEXCOORD0 )
{
		// Transform vertex position to clip space
		float4 scaled_pos = float4( iPos.xyz * g_land_scale.xyz, 1.0 );
		oPos = mul( scaled_pos, g_wvp_mtx );	

		// Generate texcoords
		oTc0.xy = 1.0/1024.0 + iPos.xz;
		oTc0.zw = 1.0/1024.0 + iPos.xz * 8.0;
}



////////////////////////////////////////////////////////////////////////////////////////////////////
void vs_shadowed(
    in float3 iPos          : POSITION0,

    out float4 oPos	        : POSITION,
    out float4 oTc0	        : TEXCOORD0,
    out float4 oSurfacePos	: TEXCOORD1,
    out float4 oWorldPos    : TEXCOORD2 )
{
		// Transform vertex position to clip space
		float4 scaled_pos = float4( iPos.xyz * g_land_scale.xyz, 1.0 );
		oPos = mul( scaled_pos, g_wvp_mtx );	

		// Generate texcoords
		oTc0.xy = 1.0/1024.0 + iPos.xz;
		oTc0.zw = 1.0/1024.0 + iPos.xz * 8.0;

		// Transform vertex position to shadow map space
		oSurfacePos = mul( scaled_pos, g_shadow_mtx );	
		
		// Transform vertex position to world space
    oWorldPos = mul( scaled_pos, g_world_mtx );
    oWorldPos.w = saturate( (oPos.z - 10.0) * 0.004 );
}



////////////////////////////////////////////////////////////////////////////////////////////////////
void vs_scaling(
    in float3 iPos          : POSITION0,

    out float4 oPos	        : POSITION,
    out float4 oTc0	        : TEXCOORD0,
    out float4 oSurfacePos	: TEXCOORD1,
    out float4 oWorldPos    : TEXCOORD2 )
{
		// Transform vertex position to clip space
		float4 scaled_pos = float4( iPos.xyz * g_land_scale.xyz, 1.0 );
		scaled_pos.y *= g_land_scale.w;
		oPos = mul( scaled_pos, g_wvp_mtx );	

		// Generate texcoords
		oTc0.xy = iPos.xz;
		oTc0.zw = 1.0/1024.0 + iPos.xz * 8.0;

		// Transform vertex position to shadow map space
		oSurfacePos = mul( scaled_pos, g_shadow_mtx );	
		
		// Transform vertex position to world space
    oWorldPos = mul( scaled_pos, g_world_mtx );
    oWorldPos.w = saturate( (oPos.z - 100.0) * 0.0004 );
}



// Pixel shaders
////////////////////////////////////////////////////////////////////////////////////////////////////
float4 ps_main( in float4 iTc0 : TEXCOORD0 ) : COLOR
{
		// Read textures and unpack
		float4 normal_and_ao = tex2D( g_normal_samp, iTc0.xy );
		float3 nrm = normalize( normal_and_ao.xzy*2.0 - 1.0 );
		float ao = normal_and_ao.w;
		float4 env_light = texCUBE( g_env_samp, nrm );
		
		// Blend textures
		float4 grass = tex2D( g_grass_samp, iTc0.zw );
		float4 rock = tex2D( g_rock_samp, iTc0.zw );
		float4 albedo = lerp( rock, grass, saturate( (nrm.y - 0.85) * 6.0 ) );
			
		// Composite and return
		return albedo * env_light * ao;
}



////////////////////////////////////////////////////////////////////////////////////////////////////
float4 ps_shadowed( in float4 iTc0        : TEXCOORD0,
										in float4 iSurfacePos : TEXCOORD1,
										in float4 iWorldPos   : TEXCOORD2 ) : COLOR
{
		// Read textures and unpack
		float3 noise0 = tex2D( g_noise_samp, iTc0.zw * 3.0f ).xyz;
		float3 noise1 = tex2D( g_noise_samp, iTc0.zw * 9.0f ).xyz;
		float3 noise2 = tex2D( g_noise_samp, iTc0.zw * 27.0f ).xyz;
		float3 noise_normal0 = lerp( noise0 * 2.0 - 1.0, float3( 0.0, 1.0, 0.0 ), 0.65 );
		float3 noise_normal1 = lerp( noise1 * 2.0 - 1.0, float3( 0.0, 1.0, 0.0 ), 0.65 );
		float3 noise_normal2 = lerp( noise2 * 2.0 - 1.0, float3( 0.0, 1.0, 0.0 ), 0.65 );
		float4 normal_and_ao = tex2D( g_normal_samp, iTc0.xy );
		float3 normal = normal_and_ao.xzy*2.0 - 1.0;
		float3 nrm_light = normalize( (noise_normal0 + noise_normal1) * 0.5 + normal );
		float3 nrm_color = normalize( (noise_normal0 + noise_normal1 + noise_normal2) * 0.67 + normal );
		float ao = normal_and_ao.w * 0.85 + 0.15;
		float4 env_light = texCUBE( g_env_samp, nrm_light );
		
		// Compute specular components
		float3 eye = normalize( g_cam_pos - iWorldPos );
		float3 h = normalize( eye + -g_sun_dir );
		
		// Add shadowed sunlight
		iSurfacePos.xyz /= iSurfacePos.w;
		float shadowmap_depth = tex2D( g_shadowmap_samp, iSurfacePos.xy ).r;
		float shadow = (shadowmap_depth + 0.00075 > iSurfacePos.z) ? 1.0 : 0.0;		
		float sun_diffuse = pow( saturate( dot( nrm_light, -g_sun_dir ) ), 2.0 );
		float sun_specular = pow( saturate( dot( nrm_light, h ) ), 16.0 ) * 0.25;
		env_light += float4( 6.0, 4.0, 1.5, 0.0 ) * (sun_diffuse + sun_specular) * shadow;
				
		// Add ambient light
		float3 amb_dir = float3( g_sun_dir.x, -g_sun_dir.y, g_sun_dir.z );
		float4 amb_light = float4( 0.0, 0.5, 1.0, 0.0 ) * pow( saturate( dot( nrm_light, amb_dir ) ), 4.0 ) * 2.0;
		env_light += amb_light;
		
		// Blend textures	
		float3 noise_ground0 = tex2D( g_noise_samp, iTc0.xy*4.0 ).xyz;
		float3 noise_ground1 = tex2D( g_noise_samp, iTc0.xy*1.0 ).xyz;
		float4 grass = tex2D( g_grass_samp, iTc0.zw*4.0 ) * float4( 0.07, 0.09, 0.05, 1.0 );
		float4 dirt = tex2D( g_dirt_samp, iTc0.zw ) * float4( 0.2, 0.25, 0.2, 1.0 );
		float4 rock = tex2D( g_rock_samp, iTc0.zw ) * float4( 1.0, 0.8, 0.6, 1.0 );
		float4 ground = lerp( dirt, grass, (noise_ground0.r + noise_ground1.r) * 0.5 );
		float4 mountain = rock * (noise_ground0.r+0.5);
		float4 albedo = lerp( mountain, ground, saturate( (nrm_color.y - 0.87) * 10.0 ) );
			
		// Composite and return
		return lerp( albedo * env_light * ao, float4( 0.7, 0.7, 0.7, 1.0 ), iWorldPos.w );
}



////////////////////////////////////////////////////////////////////////////////////////////////////
float4 get_awesome_textured( sampler2D samp, float3 tc, float3 normal )
{
		// Sample texture planes
		float4 yz = tex2D( samp, tc.yz );
		float4 xz = tex2D( samp, tc.xz + float2( 0.37, 0.71 ) );
		float4 xy = tex2D( samp, tc.xy + float2( 0.53, 0.17 ) );
		
		// Compute weights
		float3 weights = abs( normal.xyz );
		float sum = dot( weights, float3( 1.0, 1.0, 1.0 ) );
		weights /= sum;
		
		// Blend
		float4 final = yz * weights.x;
		final += xz * weights.y;
		final += xy * weights.z;
		
		// Done!
		return final;
}



////////////////////////////////////////////////////////////////////////////////////////////////////
float4 ps_scaling( in float4 iTc0        : TEXCOORD0,
								   in float4 iSurfacePos : TEXCOORD1,
									 in float4 iWorldPos   : TEXCOORD2 ) : COLOR
{
		//float s = dot( tex2D( g_shadowtex_samp, iTc0.xy ).rgb, float3(0.333, 0.334, 0.333 ) );
		//return float4( s, s, s, 1.0 );
		//return tex2D( g_shadowtex_samp, iTc0.xy );

		// Read textures and unpack
		float3 noise0 = tex2D( g_noise_samp, iTc0.zw * 3.0f ).xyz;
		float3 noise1 = tex2D( g_noise_samp, iTc0.zw * 9.0f ).xyz;
		float3 noise2 = tex2D( g_noise_samp, iTc0.zw * 27.0f ).xyz;
		float3 noise_normal0 = lerp( noise0 * 2.0 - 1.0, float3( 0.0, 1.0, 0.0 ), 0.65 );
		float3 noise_normal1 = lerp( noise1 * 2.0 - 1.0, float3( 0.0, 1.0, 0.0 ), 0.65 );
		float3 noise_normal2 = lerp( noise2 * 2.0 - 1.0, float3( 0.0, 1.0, 0.0 ), 0.65 );
		float4 normal_and_ao = tex2D( g_normal_samp, iTc0.xy );
		float3 normal = lerp( float3( 0.0, 1.0, 0.0 ), normal_and_ao.xzy*2.0 - 1.0, g_land_scale.w );
		float3 nrm_light = normalize( (noise_normal0 + noise_normal1) * 0.5 + normal );
		float3 nrm_color = normalize( (noise_normal0 + noise_normal1 + noise_normal2) * 0.67 + normal );
		float ao = lerp( 1.0, normal_and_ao.w * 0.75 + 0.25, g_land_scale.w );
		float4 env_light = texCUBE( g_env_samp, nrm_light ) * 0.70;
		
		// Compute specular components
		float3 eye = normalize( g_cam_pos - iWorldPos );
		float3 h = normalize( eye + -g_sun_dir );
		
		// Add shadowed sunlight
		//iSurfacePos.xyz /= iSurfacePos.w;
		//float shadowmap_depth = tex2D( g_shadowmap_samp, iSurfacePos.xy ).r;
		//float shadow = (shadowmap_depth + 0.00075 > iSurfacePos.z) ? 1.0 : 0.0;		
		
		float shadow = dot( tex2D( g_shadowtex_samp, iTc0.xy ).rgb, float3( 0.333, 0.334, 0.333 ) );
		
		float sun_diffuse = pow( saturate( dot( nrm_light, -g_sun_dir ) ), 2.0 );
		float sun_specular = pow( saturate( dot( nrm_light, h ) ), 16.0 ) * 0.25;
		//env_light += float4( 6.0, 4.0, 2.5, 0.0 ) * (sun_diffuse + sun_specular) * shadow;
		env_light += float4( 7.0, 5.0, 3.0, 0.0 ) * (sun_diffuse + sun_specular) * shadow * 1.75;
				
		// Add ambient light
		float3 amb_dir = float3( g_sun_dir.x, -g_sun_dir.y, g_sun_dir.z );
		float4 amb_light = float4( 0.2, 0.5, 1.0, 0.0 ) * pow( saturate( dot( nrm_light, amb_dir ) ), 2.0 );
		env_light += amb_light;
		
		// Blend textures	
		float beach_factor0 = saturate(1.40-iWorldPos.y*6.0);
		float beach_factor1 = saturate(0.70-iWorldPos.y*4.0);
		float3 noise_ground0 = tex2D( g_noise_samp, iTc0.xy*4.0 ).xyz;
		float3 noise_ground1 = tex2D( g_noise_samp, iTc0.xy*1.0 ).xyz;
		float4 grass = tex2D( g_grass_samp, iTc0.zw*4.0 ) * float4( 0.07, 0.09, 0.05, 1.0 );
		float4 dirt = tex2D( g_dirt_samp, iTc0.zw*4.0 ) * float4( 0.2, 0.25, 0.2, 1.0 );
		//float4 rock = tex2D( g_rock_samp, iTc0.zw ) * lerp( float4( 0.8, 0.6, 0.4, 1.0 ), float4( 0.40, 0.30, 0.10, 1.0 ), beach_factor0 );
		float4 rock = get_awesome_textured( g_rock_samp, iWorldPos.xyz*0.05, normal_and_ao.xzy*2.0 - 1.0 );
		rock = saturate( rock - 0.175 ) * 1.5;
		rock *= lerp( float4( 0.8, 0.6, 0.4, 1.0 ), float4( 0.40, 0.30, 0.10, 1.0 ), beach_factor0 );
		float4 ground = lerp( dirt, grass, (noise_ground0.r + noise_ground1.r) * 0.5 );
		//float4 mountain = rock * (noise_ground0.r+0.5);
		//float4 albedo = lerp( mountain, ground, saturate( (nrm_color.y - 0.86) * 10.0 - beach_factor1 ) );
		float4 albedo = lerp( rock, ground, saturate( (nrm_color.y - 0.86) * 10.0 - beach_factor1 ) );
					
		// Composite and return
		float4 final = albedo * env_light * ao;
		if( iWorldPos.y < 2.0 )
		{
				final *= 1.0 - (0.4 * saturate( 2.0-iWorldPos.y * 2.50 ) );
				final = lerp( final, float4( 0.03, 0.025, 0.0125, 1.0 ), saturate( 0.25-iWorldPos.y*0.435 ) );
		}
		final.rgb = lerp( final.rgb, float3( 0.7, 0.7, 0.7 ), iWorldPos.w );
		return float4( final.rgb, saturate( -iWorldPos.y ) );
}



////////////////////////////////////////////////////////////////////////////////////////////////////
float4 ps_regular( in float4 iTc0        : TEXCOORD0,
								   in float4 iSurfacePos : TEXCOORD1,
									 in float4 iWorldPos   : TEXCOORD2 ) : COLOR
{
		float3 tc = iWorldPos.xyz * 0.1;
		return tex2D( g_rock_samp, tc.xz );
}



////////////////////////////////////////////////////////////////////////////////////////////////////
float4 ps_awesome( in float4 iTc0        : TEXCOORD0,
								   in float4 iSurfacePos : TEXCOORD1,
									 in float4 iWorldPos   : TEXCOORD2 ) : COLOR
{
		// Sample normal
		float3 normal = normalize( tex2D( g_normal_samp, iTc0.xy ).xzy * 2.0 - 1.0 );

		// Sample texture awesomely
		return get_awesome_textured( g_rock_samp, iWorldPos.xyz * 0.1, normal );
}



// Technique
////////////////////////////////////////////////////////////////////////////////////////////////////
technique zpass
{
    pass p0 
    {
				ZEnable = true;
				AlphaBlendEnable = false;
				AlphaTestEnable = false;
        VertexShader = compile vs_3_0 vs_zpass();
        PixelShader = NULL;
    }
}



////////////////////////////////////////////////////////////////////////////////////////////////////
technique main
{
    pass p0 
    {
				ZEnable = true;
		AlphaBlendEnable = false;
        VertexShader = compile vs_3_0 vs_main();
        PixelShader = compile ps_3_0 ps_main();
    }
}



////////////////////////////////////////////////////////////////////////////////////////////////////
technique shadowed
{
    pass p0 
    {
				ZEnable = true;
		AlphaBlendEnable = false;
        VertexShader = compile vs_3_0 vs_shadowed();
        PixelShader = compile ps_3_0 ps_shadowed();
    }
}



////////////////////////////////////////////////////////////////////////////////////////////////////
technique scaling
{
    pass p0 
    {
				ZEnable = true;
		AlphaBlendEnable = false;
        VertexShader = compile vs_3_0 vs_scaling();
        PixelShader = compile ps_3_0 ps_scaling();
    }
}



////////////////////////////////////////////////////////////////////////////////////////////////////
technique regular
{
    pass p0 
    {
				ZEnable = true;
		AlphaBlendEnable = false;
        VertexShader = compile vs_3_0 vs_scaling();
        PixelShader = compile ps_3_0 ps_regular();
    }
}



////////////////////////////////////////////////////////////////////////////////////////////////////
technique awesome
{
    pass p0 
    {
				ZEnable = true;
		AlphaBlendEnable = false;
        VertexShader = compile vs_3_0 vs_scaling();
        PixelShader = compile ps_3_0 ps_awesome();
    }
}
