////////////////////////////////////////////////////////////////////////////////////////////////////
// water.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;
float g_time;



// T E X T U R E S
////////////////////////////////////////////////////////////////////////////////////////////////////
texture2D g_reflection_tex;
texture2D g_refraction_tex;
texture2D g_normal_tex;
texture2D g_shadowmap_tex;
texture2D g_shadowtex_tex;



// S A M P L E R S
////////////////////////////////////////////////////////////////////////////////////////////////////
sampler2D g_reflection_samp = sampler_state 
{
		texture = <g_reflection_tex>;
		MinFilter = LINEAR;
		MagFilter = LINEAR;
		MipFilter = NONE;
		AddressU  = CLAMP;        
		AddressV  = CLAMP;
		AddressW  = CLAMP;
};

sampler2D g_refraction_samp = sampler_state 
{
		texture = <g_refraction_tex>;
		MinFilter = LINEAR;
		MagFilter = LINEAR;
		MipFilter = NONE;
		AddressU  = CLAMP;        
		AddressV  = CLAMP;
		AddressW  = CLAMP;
};

sampler2D g_normal_samp = sampler_state 
{
		texture = <g_normal_tex>;
		MaxAnisotropy = 4;
		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;
};

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_water(
    in float3 iPos : POSITION0,

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

		// Transform vertex position to shadow map space
		oSurfacePos = mul( vPos, g_shadow_mtx );	
		
		// Transform vertex position to world space
    oWorldPos = mul( vPos, g_world_mtx );
    oWorldPos.w = saturate( (oPos.z - 100.0) * 0.0004 );
    
		// Generate texcoords
		oTc0.xy = oWorldPos.xz*0.05 - g_time*1.25;
		oTc0.zw = oWorldPos.xz*0.22 + g_time*2.5;
		
		// Copy projected texcoords
		oProjected = oPos;
}



// Vertex shaders
////////////////////////////////////////////////////////////////////////////////////////////////////
void vs_water2(
    in float3 iPos : POSITION0,

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

		// Transform vertex position to shadow map space
		oSurfacePos.xy = iPos.xz*(1.0f/512.0f);	
		oSurfacePos.zw = iPos.xz*(1.0f/512.0f);	
		
		// Transform vertex position to world space
    oWorldPos = mul( vPos, g_world_mtx );
    oWorldPos.w = saturate( (oPos.z - 100.0) * 0.0004 );
    
		// Generate texcoords
		oTc0.xy = oWorldPos.xz*0.05 - g_time*1.25;
		oTc0.zw = oWorldPos.xz*0.22 + g_time*2.5;
		
		// Copy projected texcoords
		oProjected = oPos;
}



// Pixel shaders
////////////////////////////////////////////////////////////////////////////////////////////////////
float4 ps_water( in float4 iTc0        : TEXCOORD0,
								 in float4 iSurfacePos : TEXCOORD1,
								 in float4 iWorldPos   : TEXCOORD2,
								 in float4 iProjected  : TEXCOORD3 ) : COLOR
{
		// Read textures and unpack
		float3 normal0 = tex2D( g_normal_samp, iTc0.xy ).xzy*2.0 - 1.0;
		float3 normal1 = tex2D( g_normal_samp, iTc0.zw ).xzy*2.0 - 1.0;
		float3 normal = normalize( normal0 + normal1 );
		
		// Compute reflection vector components
		float2 projected_tc = (iProjected.xy / iProjected.w) * 0.5 + 0.5;
		projected_tc.y = 1.0 - projected_tc.y;
		float distortion_scale = tex2D( g_reflection_samp, projected_tc.xy ).a;
		float2 distortion = normal.xz*0.025*distortion_scale - float2( 0.0, 0.0075 );
		float4 reflection = tex2D( g_reflection_samp, projected_tc.xy - distortion );
		
		// Compute shadow
		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;		
		
		// Compute specular
		float3 eye = normalize( g_cam_pos.xyz - iWorldPos.xyz );
		float3 h = normalize( eye + -g_sun_dir );
		float ndoth = saturate( dot( normal, h ) );
		float sun_specular = pow( ndoth, 32.0 ) * 1.0;
		sun_specular += pow( ndoth, 1024.0 ) * 20.0;
		reflection += float4( 6.0, 5.0, 3.0, 0.0 ) * sun_specular * shadow;
		
		// Compute fresnel
		float fresnel = pow( 1.0 - saturate( dot( eye, normal ) ), 3.0 ) * 0.9;
			
		// Composite and return
		return float4( lerp( reflection.rgb, float3( 0.7, 0.7, 0.7 ), iWorldPos.w ), fresnel );
}



////////////////////////////////////////////////////////////////////////////////////////////////////
float4 ps_water_refract( in float4 iTc0        : TEXCOORD0,
												 in float4 iSurfacePos : TEXCOORD1,
												 in float4 iWorldPos   : TEXCOORD2,
												 in float4 iProjected  : TEXCOORD3 ) : COLOR
{
		// Read textures and unpack
		float3 normal0 = tex2D( g_normal_samp, iTc0.xy ).xzy*2.0 - 1.0;
		float3 normal1 = tex2D( g_normal_samp, iTc0.zw ).xzy*2.0 - 1.0;
		float3 normal = normalize( normal0 + normal1 );

		// Compute eye vector
		float3 cam_to_surface = g_cam_pos.xyz - iWorldPos.xyz;
		float d = length( cam_to_surface );
		float3 eye = cam_to_surface / d;
		
		// Compute reflection vector components
		float2 projected_tc = (iProjected.xy / iProjected.w) * 0.5 + 0.5;
		projected_tc.y = 1.0 - projected_tc.y;
		float distortion_scale = tex2D( g_refraction_samp, projected_tc.xy ).a;
		float2 distortion = normal.xz * 0.025 * distortion_scale;
		normal.xz *= distortion_scale;
	  normal = normalize( normal );
		float4 reflection = tex2D( g_reflection_samp, projected_tc.xy - (distortion - float2( 0.0, 0.0075 ) ) );
		//float4 refraction = tex2D( g_refraction_samp, projected_tc.xy - distortion );
		float4 refraction = float4(0.0, 0.0, 0.0, 1.0);
		refraction.r += tex2D( g_refraction_samp, projected_tc.xy - distortion*1.2 ).r;
		refraction.g += tex2D( g_refraction_samp, projected_tc.xy - distortion*1.0 ).g;
		refraction.b += tex2D( g_refraction_samp, projected_tc.xy - distortion*0.8 ).b;
		
		// Compute shadow
		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;		
		
		// Compute specular
		float3 h = normalize( eye + -g_sun_dir );
		float ndoth = saturate( dot( normal, h ) );
		float sun_specular = pow( ndoth, 32.0 ) * 1.0;
		sun_specular += pow( ndoth, 1024.0 ) * 20.0;
		reflection += float4( 6.0, 5.0, 3.0, 0.0 ) * sun_specular * shadow;
		
		// Compute fresnel
		float fresnel = pow( 1.0 - saturate( dot( eye, normal ) ), 3.0 ) * 0.60 + 0.01;
			
		// Composite and return
		float3 final = lerp( refraction.rgb, reflection.rgb*float4( 0.8, 0.9, 1.0, 1.0 ), fresnel*saturate(distortion_scale*10.0) );
		return float4( lerp( final, float3( 0.7, 0.7, 0.7 ), iWorldPos.w ), 1.0 );
}



////////////////////////////////////////////////////////////////////////////////////////////////////
float4 ps_water_refract_soft_shadow( in float4 iTc0        : TEXCOORD0,
																		 in float4 iSurfacePos : TEXCOORD1,
																		 in float4 iWorldPos   : TEXCOORD2,
																		 in float4 iProjected  : TEXCOORD3 ) : COLOR
{
		// Read textures and unpack
		float3 normal0 = tex2D( g_normal_samp, iTc0.xy ).xzy*2.0 - 1.0;
		float3 normal1 = tex2D( g_normal_samp, iTc0.zw ).xzy*2.0 - 1.0;
		float3 normal = normalize( normal0 + normal1 );

		// Compute eye vector
		float3 cam_to_surface = g_cam_pos.xyz - iWorldPos.xyz;
		float d = length( cam_to_surface );
		float3 eye = cam_to_surface / d;
		
		// Compute reflection vector components
		float2 projected_tc = (iProjected.xy / iProjected.w) * 0.5 + 0.5;
		projected_tc.y = 1.0 - projected_tc.y;
		float distortion_scale = tex2D( g_refraction_samp, projected_tc.xy ).a;
		float2 distortion = normal.xz * 0.025 * distortion_scale;
		normal.xz *= distortion_scale;
	  normal = normalize( normal );
		float4 reflection = tex2D( g_reflection_samp, projected_tc.xy - (distortion - float2( 0.0, 0.0075 ) ) );
		//float4 refraction = tex2D( g_refraction_samp, projected_tc.xy - distortion );
		float4 refraction = float4(0.0, 0.0, 0.0, 1.0);
		refraction.r += tex2D( g_refraction_samp, projected_tc.xy - distortion*1.2 ).r;
		refraction.g += tex2D( g_refraction_samp, projected_tc.xy - distortion*1.0 ).g;
		refraction.b += tex2D( g_refraction_samp, projected_tc.xy - distortion*0.8 ).b;
		
		// Compute shadow
		float shadow = dot( tex2D( g_shadowtex_samp, iSurfacePos.xy ).rgb, float3( 0.333, 0.334, 0.333 ) );
		
		// Compute specular
		float3 h = normalize( eye + -g_sun_dir );
		float ndoth = saturate( dot( normal, h ) );
		float sun_specular = pow( ndoth, 32.0 ) * 1.0;
		sun_specular += pow( ndoth, 1024.0 ) * 20.0;
		reflection += float4( 6.0, 5.0, 3.0, 0.0 ) * sun_specular * shadow;
		
		// Compute fresnel
		float fresnel = pow( 1.0 - saturate( dot( eye, normal ) ), 3.0 ) * 0.60 + 0.01;
			
		// Composite and return
		float3 final = lerp( refraction.rgb, reflection.rgb*float4( 0.8, 0.9, 1.0, 1.0 ), fresnel*saturate(distortion_scale*10.0) );
		return float4( lerp( final, float3( 0.7, 0.7, 0.7 ), iWorldPos.w ), 1.0 );
}



// Technique
////////////////////////////////////////////////////////////////////////////////////////////////////
technique water
{
    pass p0 
    {
		AlphaBlendEnable = true;
        VertexShader = compile vs_3_0 vs_water();
        PixelShader = compile ps_3_0 ps_water();
    }
}



////////////////////////////////////////////////////////////////////////////////////////////////////
technique water_refract
{
    pass p0 
    {
		AlphaBlendEnable = false;
        VertexShader = compile vs_3_0 vs_water();
        PixelShader = compile ps_3_0 ps_water_refract();
    }
}



////////////////////////////////////////////////////////////////////////////////////////////////////
technique water_soft_shadow
{
    pass p0 
    {
		AlphaBlendEnable = false;
        VertexShader = compile vs_3_0 vs_water2();
        PixelShader = compile ps_3_0 ps_water_refract_soft_shadow();
    }
}

