
//
// Water shader.
// Big, fat and ugly.
//
// All (most) things considered, I have converged to this particular way of rendering water:
//
//   Vertex waves
//   No transparency. Solid color for deep water.
//   Fresnel law, reflects environment.
//   Phong speculars.
//   Ripples via animated normal map. Adjustable intensity, speed and scale. Affect reflection and speculars.

#include "common.h"

uniform float4x4 WorldMatrix;

uniform float4 nmAnimLerp; // ratio between normal map frames
uniform float4 waveParams; // .x = frequency  .y = phase  .z = height
uniform float4 WaterColor; // deep water color

#ifdef PIN_HQ
#	define WATER_LOD 1
#else
#	define WATER_LOD 2
#endif

#define LODBIAS (-1)

float  fadeFactor( float3 wspos )
{
	return saturate( -0.4f + 1.4f*length( G.CameraPosition.xyz - wspos.xyz ) * G.FadeDistance.y );
}

float  wave( float4 wspos )
{
	float x = sin( ( wspos.z - wspos.x - waveParams.y ) * waveParams.x );
	float z = sin( ( wspos.z + wspos.x + waveParams.y ) * waveParams.x );
	float p = (x + z) * waveParams.z;
	return  p - p * fadeFactor( wspos.xyz );
}



// perturbs the water mesh and vertex normals
void makeWaves( inout float4 wspos, inout float3 wsnrm )
{
#if WATER_LOD == 0 
	float gridSize = 4.0f;

	float4 wspos1 = wspos;
	float4 wspos2 = wspos;

	wspos1.x += gridSize;
	wspos2.z += gridSize;
	
	wspos.y  += wave(wspos) ;
	wspos1.y += wave(wspos1);
	wspos2.y += wave(wspos2);
	
	wsnrm = normalize( cross( wspos2.xyz - wspos.xyz, wspos1.xyz - wspos.xyz ) );
#elif WATER_LOD == 1
	wspos.y += wave( wspos );
#else   /* do n0thing */
#endif
}

struct V2P
{
	float4 pos    : POSITION;
	float4 tc0Fog : TEXCOORD0;
	float4 wspos  : TEXCOORD1;
	float3 wsnrm  : TEXCOORD2;
	float3 light  : TEXCOORD3;
	float3 fade   : TEXCOORD4;
};

V2P water_vs(
	float4 pos : POSITION,
	float3 nrm : NORMAL
)
{
	V2P o;

    // Decode vertex data
    nrm = (nrm - 127) / 127;

	nrm = normalize(nrm);
	
	float4 wspos = mul( WorldMatrix, pos );
	float3 wsnrm = nrm;
	
	wspos.y -= 2*waveParams.z;
	
	makeWaves( /*INOUT*/ wspos, /*INOUT*/ wsnrm );
	
	o.wspos = wspos;
	o.wsnrm = wsnrm;
	
	if( nrm.y < 0.01f ) o.wsnrm = nrm;

	// box mapping
	//float3x2 m = { wspos.xz, wspos.xy, wspos.yz };
	//float2 tcselect = mul( abs( nrm.yzx ), m );

	float2 tcselect;
	float3 wspostc = float3( wspos.x, -wspos.y, wspos.z );

	tcselect.x = dot( abs( nrm.yxz ), wspostc.xzx );
	tcselect.y = dot( abs( nrm.yxz ), wspostc.zyy );

	o.pos       = mul( G.ViewProjection, wspos );
	o.tc0Fog.xy = tcselect * .05f;
	o.tc0Fog.z  = saturate( (G.FogParams.z - o.pos.w) * G.FogParams.w );
	o.tc0Fog.w  = LODBIAS;

	o.light = lgridPrepareSample(lgridOffset(wspos.xyz, wsnrm.xyz));
	
	o.fade.x = fadeFactor( wspos.xyz );
	o.fade.y = (1-o.fade.x) *  saturate( dot( wsnrm, -G.Lamp0Dir ) ) * 100;
	o.fade.z =  1 - 0.9*saturate1( exp( -0.005 * length( G.CameraPosition.xyz - wspos.xyz ) ) );
	
	return o;
}

//////////////////////////////////////////////////////////////////////////////

sampler2D NormalMap1    : register(s0);
sampler2D NormalMap2    : register(s1);
samplerCUBE EnvMap      : register(s2);
LGRID_SAMPLER LightMap  : register(s3);
sampler2D LightMapLookup: register(s4);



float3 pixelNormal( float4 tc0 )
{
	float4 nm1 = tex2Dbias( NormalMap1, tc0 );
#if WATER_LOD <= 1
	float4 nm2 = tex2Dbias( NormalMap2, tc0 );
	float3 normal = lerp( nm1, nm2, nmAnimLerp.xxxx ).agb;
#else
	float3 normal = nm1.agb;
#endif
	//normal = nm2;
	normal.xy = 2*normal.xy - 1; 
	normal.z  = sqrt(  1.001 - saturate1( dot( normal.xy, normal.xy ) ) );
	return normal;
}

// Fresnel approximation. N1 and N2 are refractive indices.
// for above water, use n1 = 1, n2 = 1.3, for underwater use n1 = 1.3, n2 = 1
float fresnel( float3 N, float3 V, float n1, float n2, float p, float fade )
{
#if WATER_LOD == 0
	float r0 = (n1-n2)/(n1+n2);
	r0 *= r0;
	return r0 + (1-r0) * pow( 1 - abs( dot( normalize(N), V ) ), p );
#else
	return 0.1 + saturate( - 1.9 * abs( dot( N, V ) ) + 0.8); // HAXX!
	//return 1 - 2 * abs( dot( N, V ) );
#endif
}

float4 envColor( float3 N, float3 V, float fade )
{
	float3 dir = reflect( V, N );
    return texCUBE(EnvMap, dir) * 0.91f;
}

float4 deepWaterColor(float4 light)
{
	//float4 tint = 5*float4( 0.1f, 0.1f, 0.13f, 1);
	float4 tint = 0.8f*float4( 118, 143, 153, 255 ) / 255;
	return (light + texCUBEbias( EnvMap, float4( 0,1,0, 10.0f) )) * tint;
}


//////////////////////////////////////////
//////////////////////////////////////////


 
float4 water_ps( V2P v ) : COLOR0
{

	float4 WaterColorTest = 0.5 * float4(  26, 169, 185, 0  ) / 255;
	float4 FogColorTest = 0.8 * float4( 35, 107, 130, 0 ) / 255;
	
	float3 N2 = v.wsnrm;
	float3 N1 = pixelNormal( v.tc0Fog ).xzy;
	float3 N3 = 0.5*(N2 +  N1);
	
	N3 = lerp( N3, N2, v.fade.z );

	float3 L = /*normalize*/(-G.Lamp0Dir.xyz);
	float3 E = normalize( G.CameraPosition.xyz - v.wspos.xyz );

	float4 light = lgridSample(LightMap, LightMapLookup, v.light.xyz);
	
	float  fre     =  fresnel( N3, E, 1.0f, 1.3f, 5, v.fade.x );
	float3 diffuse =  deepWaterColor(light).rgb;
	float3 env     =  envColor( N3, -E, v.fade.x ).rgb;

	float3 R = reflect( -L, N1 );  
	
#if WATER_LOD <= 1
	float specular = pow( saturate0( dot( R, E ) ), 1600 ) * L.y * 100; // baseline
#	ifndef GLSLES
		specular = 0.65 * saturate1( specular * saturate0( light.a - 0.4f ) );
#	endif
#else
	float specular = 0;
#endif

	float3 result = lerp( diffuse, env, fre ) + specular.xxx;
	result = lerp( G.FogColor.rgb, result, v.tc0Fog.z );
		
	return float4( result, 1 );
}
