// Software pixel shader generated by Wild Magic.
//
// var float2    kBumpTCoord0 $vin.TEXCOORD0
// var float2    kBumpTCoord1 $vin.TEXCOORD1
// var float3    kViewDir     $vin.TEXCOORD2
// var float3    kTangent     $vin.TEXCOORD3
// var float3    kBinormal    $vin.TEXCOORD4
// var float3    kNormal      $vin.TEXCOORD5
// var float4    kLightDir    $vin.TEXCOORD6
// var float4    kPixelColor  $vout.COLOR
// var sampler2D BumpSampler  texunit 0
// var sampler1D WaterSampler texunit 1
// var sampler2D EnvSampler   texunit 2

#include "Wm4SoftRenderer.h"
#include "Wm4ColorRGBA.h"
#include "Wm4Vector3.h"
#include "Wm4Vector4.h"

namespace Wm4
{

ColorRGBA p_RipplingOcean (const float*, SoftSampler** apkSampler,
    const float* afInPixel)
{
    // Get samplers.
    SoftSampler& rkBumpSampler = *apkSampler[0];
    SoftSampler& rkWaterSampler = *apkSampler[1];
    SoftSampler& rkEnvSampler = *apkSampler[2];

    // Get the input items.
    const float* afBumpTCoord0 = &afInPixel[0];
    const float* afBumpTCoord1 = &afInPixel[2];
    const Vector3f& rkViewDir = *(const Vector3f*)&afInPixel[4];
    const Vector3f& rkTangent = *(const Vector3f*)&afInPixel[7];
    const Vector3f& rkBinormal = *(const Vector3f*)&afInPixel[10];
    const Vector3f& rkNormal = *(const Vector3f*)&afInPixel[13];
    const Vector4f& rkLightDir = *(const Vector4f*)&afInPixel[16];

    // *** program ***

    // A lot of this shader is making the water look just right.  It looks
    // pretty good in general, but there are some tweaks.  I will try to
    // explain what is necessary and what is specific to this case.

    // Sample the bumpmap twice.
    ColorRGBA kNpColor0 = rkBumpSampler(afBumpTCoord0);
    ColorRGBA kNpColor1 = rkBumpSampler(afBumpTCoord1);
    Vector3f kNormPerturb0(
        2.0f*kNpColor0[0] - 1.0f,
        2.0f*kNpColor0[1] - 1.0f,
        2.0f*kNpColor0[2] - 1.0f);
    Vector3f kNormPerturb1(
        2.0f*kNpColor1[0] - 1.0f,
        2.0f*kNpColor1[1] - 1.0f,
        2.0f*kNpColor1[2] - 1.0f);

    // The perturbed normal (in bumpmap space) is going to be the average.
    Vector3f kNormPerturb = (kNormPerturb0 + kNormPerturb1)*0.5f;

    // Because the waves may have changed the surface, we will transform the
    // bump mapped normal into world space.  We cannot just use the
    // model->world transform here because that applies to the original
    // model.  Because that got changed (along with the normal) in the
    // vertex shader, we have to do it this way.
    Vector3f kNewNormal = kNormPerturb.X()*rkTangent +
        kNormPerturb.Y()*rkBinormal + kNormPerturb.Z()*rkNormal;
    kNewNormal.Normalize();

    // The water color is view dependent. We look this up in the gradient
    // texture.  Using the old normal (the water surface normal before bump
    // mapping) makes the Fresnel factor (and the water color) look more
    // right because it is much more low frequency than the bump mapped
    // ripples.
    float fFresnel = 1.0f - Saturate(-(rkNormal.Dot(rkViewDir)));

    // This step could have been done in the texture itself.
    float fFresnelCubed = Mathf::Pow(fFresnel,3.0f);
    
    // Get the water color from the gradient texture.  If we are looking
    // tangentially at the water, it will be bluer.  If we are looking
    // straight down (fFresnel close to zero) it will be greener.
    ColorRGBA kTmp = rkWaterSampler(&fFresnelCubed);
    Vector3f kWaterColor(kTmp[0],kTmp[1],kTmp[2]);

    // Get the reflection vector for specular reflections.
    Vector3f kReflect = Reflect(kNewNormal,rkViewDir);

    // We're going to sample the background texture, but we need to 
    // put the coordinates into [0,1].
    kReflect = MapToUnit(kReflect);

    // This step is just due to the texture that we are using.  It biases
    // towards the top, so that less of the water reflects the sun.  With an
    // an environment map, this is irrelevant.  A sphere map was tried, but
    // it was hard to get it just right.  Using the background texture is a
    // hack, but it looks good.
    kReflect.Z() = kReflect.Z()*0.7f + 0.2f;

    // This step is dependent upon knowing that up is in the z direction and
    // y is the forwards/backwards direction and x is left to right.  If we
    // had an environment map, it would not be an issue.
    float afReflectTCoord[2] = { kReflect.X(), kReflect.Z() };
    kTmp = rkEnvSampler(afReflectTCoord);
    Vector3f kBackground(kTmp[0],kTmp[1],kTmp[2]);

    // Use some factor of the background to find the specular reflection.
    // This factor is totally arbitrary and so the "glow" factor of the
    // background is the green component.  What I really want is the big sun
    // to be the have the strongest specular component so it will appear on
    // the water.  Green will do that.  Another way to do this would be to
    // store a glow map in the alpha channel and use that.  Also, we will use
    // Fresnel reflections as a factor so that the water reflects much more
    // when you look at it at an angle.  The specular is toned down a bit.
    float fSpecular = kBackground.Y()*kBackground.Y()*fFresnelCubed;
    
    // Calculate a diffuse factor, but we do not want it too dark, so we will
    // add a small arbitrary ambient factor.
    float fAmbient = rkLightDir.W();
    float fDiffuse = fAmbient - (1.0f-fAmbient) * (kNewNormal[0] *
        rkLightDir[0] + kNewNormal[1]*rkLightDir[1] + kNewNormal[2] *
        rkLightDir[2]);

    // Add the diffusely lit water color with some specular highlights.
    Vector3f kPixelColor = fDiffuse*kWaterColor + fSpecular*kBackground;
    ColorRGBA kOutColor(kPixelColor[0],kPixelColor[1],kPixelColor[2],1.0f);
    if (kOutColor[0] > 1.0f || kOutColor[1] > 1.0f
    ||  kOutColor[2] > 1.0f || kOutColor[3] > 1.0f)
    {
        int iStopHere = 0;
    }
    return kOutColor;
}

WM4_IMPLEMENT_PPROGRAM(RipplingOcean);
WM4_REGISTER_PPROGRAM(RipplingOcean);

}
