//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "../torque.hlsl"

//-----------------------------------------------------------------------------
// Defines                                                                  
//-----------------------------------------------------------------------------

// miscParams
#define FRESNEL_BIAS       miscParams[0]
#define FRESNEL_POWER      miscParams[1]
#define CLARITY            miscParams[2]
#define ISRIVER            miscParams[3]

// reflectParams
#define REFLECT_PLANE_Z    reflectParams[0]
#define REFLECT_MIN_DIST   reflectParams[1]
#define REFLECT_MAX_DIST   reflectParams[2]
#define NO_REFLECT         reflectParams[3]

// distortionParams
#define DISTORT_START_DIST distortionParams[0]
#define DISTORT_END_DIST   distortionParams[1]
#define DISTORT_FULL_DEPTH distortionParams[2]

// ConnectData.misc
#define LIGHT_VEC IN.misc.xyz
#define WORLD_Z   IN.objPos.w

// specularParams
#define SPEC_POWER         specularParams[3]
#define SPEC_COLOR         specularParams.xyz

//-----------------------------------------------------------------------------
// Structures                                                                  
//-----------------------------------------------------------------------------

struct ConnectData
{
   float4 hpos             : POSITION;   
   
   // TexCoord 0 and 1 (xy,zw) for ripple texture lookup
   float4 rippleTexCoord01 : TEXCOORD0;   
   
   // TexCoord 2 for ripple texture lookup
   float2 rippleTexCoord2  : TEXCOORD1;
   
   // Screenspace vert position BEFORE wave transformation
   float4 posPreWave       : TEXCOORD2;
   
   // Screenspace vert position AFTER wave transformation
   float4 posPostWave      : TEXCOORD3;   
   
   // Worldspace unit distance/depth of this vertex/pixel
   float  pixelDist        : TEXCOORD4;   
   
   // Objectspace vert position BEFORE wave transformation	
   // w coord is world space z position.
   float4 objPos           : TEXCOORD5; 
   
   float3 misc             : TEXCOORD6;   
};

//-----------------------------------------------------------------------------
// approximate Fresnel function
//-----------------------------------------------------------------------------
float fresnel(float NdotV, float bias, float power)
{
   return bias + (1.0-bias)*pow(abs(1.0 - max(NdotV, 0)), power);
}

//-----------------------------------------------------------------------------
// Uniforms                                                                  
//-----------------------------------------------------------------------------
uniform sampler      bumpMap     : register( S0 );
//uniform sampler2D    prepassTex  : register( S1 );
uniform sampler2D    reflectMap  : register( S2 );
uniform sampler      refractBuff : register( S3 );
uniform samplerCUBE  skyMap      : register( S4 );
//uniform sampler      foamMap     : register( S5 );
uniform float4       baseColor;
uniform float4       miscParams;
uniform float4       reflectParams;
uniform float3       ambientColor;
uniform float3       eyePos;
uniform float3       distortionParams;
uniform float3       fogData;
uniform float4       fogColor;
uniform float4       rippleMagnitude;
uniform float4       specularParams;
uniform float4x4     modelMat;

//-----------------------------------------------------------------------------
// Main                                                                        
//-----------------------------------------------------------------------------
float4 main( ConnectData IN ) : COLOR
{ 
   // Modulate baseColor by the ambientColor.
   float4 waterBaseColor = baseColor * float4( ambientColor.rgb, 1 );
   
   // Get the bumpNorm...
   float3 bumpNorm = ( tex2D( bumpMap, IN.rippleTexCoord01.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.x;
   bumpNorm       += ( tex2D( bumpMap, IN.rippleTexCoord01.zw ).rgb * 2.0 - 1.0 ) * rippleMagnitude.y;      
   bumpNorm       += ( tex2D( bumpMap, IN.rippleTexCoord2 ).rgb * 2.0 - 1.0 ) * rippleMagnitude.z;  
   
   bumpNorm = normalize( bumpNorm );
   bumpNorm = lerp( bumpNorm, float3(0,0,1), 1.0 - rippleMagnitude.w );
   
   // We subtract a little from it so that we don't 
   // distort where the water surface intersects the
   // camera near plane.
   float distortAmt = saturate( IN.pixelDist / 1.0 ) * 0.8;
      
   float4 distortPos = IN.posPostWave;
   distortPos.xy += bumpNorm.xy * distortAmt;   
 
 #ifdef UNDERWATER
   return hdrEncode( tex2Dproj( refractBuff, distortPos ) );   
 #else

   float3 eyeVec = IN.objPos.xyz - eyePos;
   eyeVec = mul( (float3x3)modelMat, eyeVec );
   float3 reflectionVec = reflect( eyeVec, bumpNorm ); 

   // Color that replaces the reflection color when we do not
   // have one that is appropriate.
   float4 fakeColor = float4(ambientColor,1);
   
   // Use fakeColor for ripple-normals that are angled towards the camera  
   eyeVec = -eyeVec;
   eyeVec = normalize( eyeVec );
   float ang = saturate( dot( eyeVec, bumpNorm ) );   
   float fakeColorAmt = ang;   
      
    // Get reflection map color
   float4 refMapColor = hdrDecode( tex2Dproj( reflectMap, distortPos ) ); 
   // If we do not have a reflection texture then we use the cubemap.
   refMapColor = lerp( refMapColor, texCUBE( skyMap, reflectionVec ), NO_REFLECT );      
   
   // Combine reflection color and fakeColor.
   float4 reflectColor = lerp( refMapColor, fakeColor, fakeColorAmt );
   //return refMapColor;
   
   // Get refract color
   float4 refractColor = hdrDecode( tex2Dproj( refractBuff, distortPos ) );   
   
   // calc "diffuse" color by lerping from the water color
   // to refraction image based on the water clarity.
   float4 diffuseColor = lerp( refractColor, waterBaseColor, 1.0f - CLARITY );   
   
   // fresnel calculation 
   float fresnelTerm = fresnel( ang, FRESNEL_BIAS, FRESNEL_POWER );	
   //return float4( fresnelTerm.rrr, 1 );
   
   // Also scale the frensel by our distance to the
   // water surface.  This removes the hard reflection
   // when really close to the water surface.
   fresnelTerm *= saturate( IN.pixelDist - 0.1 );
   
   // Combine the diffuse color and reflection image via the
   // fresnel term and set out output color.
   float4 OUT = lerp( diffuseColor, reflectColor, fresnelTerm );  
   
   #ifdef WATER_SPEC

      // Get some specular reflection.
      float3 newbump = bumpNorm;
      newbump.xy *= 3.5;
      newbump = normalize( bumpNorm );
      half3 halfAng = normalize( eyeVec + -LIGHT_VEC );
      float specular = saturate( dot( newbump, halfAng ) );
      specular = pow( specular, SPEC_POWER );   
      
      OUT.rgb = OUT.rgb + ( SPEC_COLOR * specular.xxx );  

   #else // Disable fogging if spec is on because otherwise we run out of instructions.
   
      // Fog it.   
      float factor = computeSceneFog( eyePos, 
                                      IN.objPos, 
                                      WORLD_Z,
                                      fogData.x,
                                      fogData.y,
                                      fogData.z );

      //OUT.rgb = lerp( OUT.rgb, fogColor.rgb, 1.0 - saturate( factor ) );   

   #endif
   
   return hdrEncode( OUT );
   
#endif   
}
