// Software vertex shader generated by Wild Magic.
//
// var float3   kModelPosition       $vin.POSITION
// var float3   kModelNormal         $vin.NORMAL
// var float3   kModelTangent        $vin.COLOR
// var float2   kWaveTCoord          $vin.TEXCOORD0
// var float4   kClipPosition        $vout.POSITION
// var float2   kBumpTCoord0         $vout.TEXCOORD0
// var float2   kBumpTCoord1         $vout.TEXCOORD1
// var float3   kViewDir             $vout.TEXCOORD2
// var float3   kTangent             $vout.TEXCOORD3
// var float3   kBinormal            $vout.TEXCOORD4
// var float3   kNormal              $vout.TEXCOORD5
// var float4   kLightDirPlusAmbient $vout.TEXCOORD6
// var float4x4 WVPMatrix            c[0]
// var float3   CameraWorldPosition  c[4]
// var float3   LightDir             c[5]
// var float4   WaveDirX             c[6]
// var float4   WaveDirY             c[7]
// var float4   WaveSpeed            c[8]
// var float4   WaveOffset           c[9]
// var float4   WaveHeight           c[10]
// var float4   BumpSpeed            c[11]
// var float4   Constants            c[12]

#include "Wm4SoftRenderer.h"
#include "Wm4Matrix4.h"
#include "Wm4Vector2.h"
#include "Wm4Vector3.h"

namespace Wm4
{

void v_RipplingOcean (const float* afRegister, const float* afInVertex,
    float* afOutVertex)
{
    // Get the register items.
    const Matrix4f& rkWVPMatrix = *(const Matrix4f*)&afRegister[0];
    const Vector3f& rkCameraWorldPosition = *(const Vector3f*)&afRegister[16];
    const Vector3f& rkLightDir = *(const Vector3f*)&afRegister[20];
    const Vector4f& rkWaveDirX = *(const Vector4f*)&afRegister[24];
    const Vector4f& rkWaveDirY = *(const Vector4f*)&afRegister[28];
    const Vector4f& rkWaveSpeed = *(const Vector4f*)&afRegister[32];
    const Vector4f& rkWaveOffset = *(const Vector4f*)&afRegister[36];
    const Vector4f& rkWaveHeight = *(const Vector4f*)&afRegister[40];
    const Vector4f& rkBumpSpeed = *(const Vector4f*)&afRegister[44];
    float fAvgDuDxDvDy = afRegister[48];
    float fAmbient = afRegister[49];
    float fTexRepeat = afRegister[50];
    float fTime = afRegister[51];

    // Get the input items.
    Vector4f kModelPosition(afInVertex[0],afInVertex[1],afInVertex[2],1.0f);
    Vector4f kModelNormal(afInVertex[3],afInVertex[4],afInVertex[5],0.0f);
    const Vector3f& rkModelTangent = *(const Vector3f*)&afInVertex[6];
    const Vector2f& rkWaveTCoord = *(const Vector2f*)&afInVertex[9];

    // Access the output items.
    Vector4f& rkClipPosition = *(Vector4f*)&afOutVertex[0];
    Vector2f& rkBumpTCoord0 = *(Vector2f*)&afOutVertex[4];
    Vector2f& rkBumpTCoord1 = *(Vector2f*)&afOutVertex[6];
    Vector3f& rkViewDir = *(Vector3f*)&afOutVertex[8];
    Vector3f& rkTangent = *(Vector3f*)&afOutVertex[11];
    Vector3f& rkBinormal = *(Vector3f*)&afOutVertex[14];
    Vector3f& rkNormal = *(Vector3f*)&afOutVertex[17];
    Vector4f& rkLightDirPlusAmbient = *(Vector4f*)&afOutVertex[20];

    // *** program ***

    // The shader has 4 waves.  Each wave has a direction (in tangent space)
    // of (kWaveDirX[i],kWaveDirY[i]) and some speed, height, and offset into
    // the sin function.
 
    // Wave position at a given time is an input to the sinusoidal function.
    Vector4f kOffset = rkWaveTCoord.X()*rkWaveDirX +
        rkWaveTCoord.Y()*rkWaveDirY + rkWaveSpeed*fTime + rkWaveOffset;

    // Map the offset components into the interval [-pi/2,pi/2).
    Vector4f kFrcOffset;
    int i;
    for (i = 0; i < 4; i++)
    {
        kFrcOffset[i] = Mathf::FMod(Mathf::FAbs(kOffset[i]),1.0f) - 0.5f;
    }
    kFrcOffset *= Mathf::TWO_PI;

    // Get the sin and cos values of the fractional offset components.
    Vector4f kSin, kCos;
    for (i = 0; i < 4; i++)
    {
        kSin[i] = Mathf::Sin(kFrcOffset[i]);
        kCos[i] = Mathf::Cos(kFrcOffset[i]);
    }

    // Add each of the four waves to the wave height.  The sin portion is the
    // wave height.  The cos part (derivative) will be the change in normal.
    float fWaveHeight = kSin.Dot(rkWaveHeight);

    // Add this wave height to the original position (along the normal).
    Vector4f kWaveModelPosition = fWaveHeight*kModelNormal + kModelPosition;
    rkClipPosition = kWaveModelPosition*rkWVPMatrix;

    // Get the cos height of the wave.
    Vector4f kCosWaveHeight;
    for (i = 0; i < 4; i++)
    {
        kCosWaveHeight[i] = kCos[i]*rkWaveHeight[i];
    }
    
    // Calculate a normal, tangent, and binormal for a coordinate system for
    // the pixel shader to use.  NOTE:  This assumes the model normal is
    // N = (0,0,-1) and the model tangent is T = (0,1,0).  The normal offset
    // is of the form (a,b,b).  The modified normal is
    //   N' = (a,b,-1)/sqrt(1+a^2+b^2)
    // and the modified tangent is
    //   T' = (0,1,b)/sqrt(1+b^2).
    // These are unit-length vectors which are orthogonal.  The binormal
    // vector is B' = Cross(N',T'), which is unit length.  The set {N',T',B'}
    // is orthonormal.
    Vector3f kNormalOffset;
    kNormalOffset.X() = -(kCosWaveHeight.Dot(rkWaveDirX));
    kNormalOffset.Y() = -(kCosWaveHeight.Dot(rkWaveDirY));
    kNormalOffset.Z() = kNormalOffset.Y();
    kNormalOffset *= fAvgDuDxDvDy;
    rkNormal = (Vector3f)kModelNormal;
    rkNormal.X() += kNormalOffset.X();
    rkNormal.Y() += kNormalOffset.Y();
    rkNormal.Normalize();
    rkTangent = MapFromUnit(rkModelTangent);
    rkTangent.Z() += kNormalOffset.Z();
    rkTangent.Normalize();
    rkBinormal = rkNormal.UnitCross(rkTangent);

    // Calculate the view direction for the vertex.
    rkViewDir = (Vector3f)kWaveModelPosition - rkCameraWorldPosition;
    rkViewDir.Normalize();

    // Create texture coordinates.  The bump maps have a speed of bumpspeed
    // which is offset from their original texture coordinates.  If you want
    // the texture to repeat on the quad more often, then ramp up TexRepeat,
    // which gives the impression of being farther away.
    rkBumpTCoord0.X() = fTime*rkBumpSpeed.X() + fTexRepeat*rkWaveTCoord.X();
    rkBumpTCoord0.Y() = fTime*rkBumpSpeed.Y() + fTexRepeat*rkWaveTCoord.Y();
    
    // Swizzle so that the textures will never line up.
    rkBumpTCoord1.Y() = fTime*rkBumpSpeed.W() + fTexRepeat*rkWaveTCoord.X();
    rkBumpTCoord1.X() = fTime*rkBumpSpeed.Z() + fTexRepeat*rkWaveTCoord.Y();

    // Return the light direction, which assumes kLightDir is normalized.
    // The w-component is used to pass through the ambient value.
    rkLightDirPlusAmbient.X() = rkLightDir.X();
    rkLightDirPlusAmbient.Y() = rkLightDir.Y();
    rkLightDirPlusAmbient.Z() = rkLightDir.Z();
    rkLightDirPlusAmbient.W() = fAmbient;
}

WM4_IMPLEMENT_VPROGRAM(RipplingOcean);
WM4_REGISTER_VPROGRAM(RipplingOcean);

}
