// Copyright 2010 Google, Inc.  All rights reserved.
// Author: chrisco@google.com (Chris Co)
//
// NOTE(chrisco) 2010-01-27: Since much of this shader code remains untouched
// from the way it was provided by IDV, the IDV copyright is kept here.

///////////////////////////////////////////////////////////////////////
//  LeafCard.fx
//
//  *** INTERACTIVE DATA VISUALIZATION (IDV) CONFIDENTIAL AND PROPRIETARY INFORMATION ***
//
//  This software is supplied under the terms of a license agreement or
//  nondisclosure agreement with Interactive Data Visualization, Inc. and
//  may not be copied, disclosed, or exploited except in accordance with
//  the terms of that agreement.
//
//      Copyright (c) 2003-2009 IDV, Inc.
//      All rights reserved in all media.
//
//      IDV, Inc.
//      Web: http://www.idvinc.com

$input "glsles.h"
$input "speedtree_configuration_glsles.h"
$input "speedtree_utils_glsles.h"

attribute vec4 ig_Vertex;
attribute vec4 ig_Normal;
attribute vec4 ig_MultiTexCoord0;
attribute vec4 ig_MultiTexCoord1;
attribute vec4 ig_MultiTexCoord2;
attribute vec4 ig_MultiTexCoord3;

uniform vec3   batch_camera_location;
uniform vec3   batch_camera_direction;
uniform vec4   batch_camera_params;
uniform vec3   lightDirection;
uniform vec4   treeRotationVector;
uniform vec4   treePosAndScale;
uniform vec4   lod_profile;
uniform vec4   materialDiffuse;

varying vec4  vout_position;
varying vec4  vout_diffuseTexCoords; // xy = diffuse texcoords, z = unused, w = alpha scalar
varying vec4  vout_normalVec;    // xyz = normal map vector
#ifdef GOOGLE_COMPUTE_FOG
varying vec4  vout_fogData;       // for sky color fogging
#endif

///////////////////////////////////////////////////////////////////////
// Creates a matrix that first rotates on the Y axis, then the Z axis.
mat3 CreateZYRotation(float fZAngle, float fYAngle) {
    // TODO(chrisco): build without the matrix multiply; saves 2 instructions.
    return mul(RotationMatrix_AroundUpAxis(fZAngle),
               RotationMatrix_yAxis(fYAngle));
}


void main()
{
    vec3  position         = ig_Vertex.xyz;   // xyz = center pos
    vec3  normal           = ig_Normal.xyz;     // xyz = normal
    vec4  texCoords        = ig_MultiTexCoord0;  // xy = diffuse,
                                              // zw = lod dimensions
    vec4  corner           = ig_MultiTexCoord1;  // xy = corner pos,
                                              // z = shadow offset,
                                              // w = planar offset
    vec4  tangent          = ig_MultiTexCoord2;  // xyz = tangent,
                                              // w = ambient occlusion
    vec4  binormal         = ig_MultiTexCoord3;  // xyz = binormal,
                                              // w = wind scalar
#ifdef SPEEDTREE_BASIC_WIND
    vec4  windData         = ig_MultiTexCoord5;  // xyzw = wind data
#endif

    // Unpack and convert the input into variables used by the original
    // SpeedTree shader.
    mat4 g_mModelViewProj = ig_ModelViewProjectionMatrix;
    vec3 g_vCameraPosition = batch_camera_location;
    vec3 g_vLightDir = lightDirection;
    vec4 g_vLodProfile = lod_profile;

    float c_fAlphaScalar = materialDiffuse.a;

    vec4 g_vTreePosAndScale = treePosAndScale;  // xyz = position, w = scale

    vec4 g_vTreeRotation = treeRotationVector;  // x = -sin A, y = cos A, z = lod value (1 = highest, 0 = lowest)

    vec3 g_vCameraDirection = batch_camera_direction;

    // Compute a camera facing matrix from the camera direction.
    mat3 g_mCameraFacingMatrix = CreateZYRotation(batch_camera_params.z, batch_camera_params.w);

    // setup aliases (taken from global variables)
    float  c_fTreeScale = g_vTreePosAndScale.w;
    vec3 c_vTreePos = g_vTreePosAndScale.xyz;
    float  c_fLod = g_vTreeRotation.w;
    float  c_fLodLerp = g_vTreeRotation.z;

    // setup aliases (taken from incoming vertex)
    vec3 c_vCardOffset = position.xyz;
    vec3 c_vNormal = normal.xyz;
    vec3 c_vTangent = tangent.xyz;
    vec3 c_vBinormal = binormal.xyz;
    vec2 c_vDiffuseTexCoords = texCoords.xy;
    vec2 c_vCorner = corner.xy;
    vec2 c_vLodScales = texCoords.zw;
    float  c_fPlanarOffset = corner.w * c_fTreeScale;

    // compensate for the instance's arbitrary rotation
    vec3 vRotatedOffset = AdjustForTreeRotation(c_vCardOffset, g_vTreeRotation);
    vec3 vRotatedNormal = AdjustForTreeRotation(c_vNormal, g_vTreeRotation);
    vec3 vRotatedTangent = AdjustForTreeRotation(c_vTangent, g_vTreeRotation);
    vec3 vRotatedBinormal = AdjustForTreeRotation(c_vBinormal, g_vTreeRotation);

    // LOD interpolation; cards are shrunk & grown, depending on the LOD setting
    float fLodScale = lerp(c_vLodScales.y, c_vLodScales.x, c_fLodLerp);
    vec2 vLodScaledCorner = c_vCorner * fLodScale;
    vec3 vCardCorner = vec3(0.0, vLodScaledCorner);

    // rotate the card to face the camera
    vCardCorner = mul(g_mCameraFacingMatrix, vCardCorner);

    // place the card according to the tree's overall scale
    vec3 vPosition = vRotatedOffset + vCardCorner.xyz;

    // move the vertex with wind
#ifdef SPEEDTREE_BASIC_WIND
    vec4 c_vWindData = windData;
    float  c_fWindScalar = binormal.w;
    vPosition = LeafWindMotion(vPosition, vRotatedNormal, c_fWindScalar);
    vec3 vWindOffset;
    vPosition = CommonWindMotion(vPosition, c_vWindData, vWindOffset);

    #ifdef SPEEDTREE_WIND_AFFECTS_LIGHTING
        vRotatedNormal += vWindOffset;
        vRotatedNormal = normalize(vRotatedNormal);
        vRotatedBinormal += vWindOffset;
        vRotatedBinormal = normalize(vRotatedBinormal);
        vRotatedTangent += vWindOffset;
        vRotatedTangent = normalize(vRotatedTangent);
    #endif
#endif

    // normal map based lighting
    vout_normalVec.x = dot(g_vLightDir, vRotatedTangent);
    vout_normalVec.y = dot(g_vLightDir, vRotatedBinormal);
    vout_normalVec.z = dot(g_vLightDir, vRotatedNormal);

    // scale the leaf card
    vPosition.xyz *= c_fTreeScale;

    // move the position to this instance's position in the forest
    vPosition.xyz += c_vTreePos;

    // adjust corner to keep all the leaf cards from being coplanar
    vPosition.xyz += c_fPlanarOffset * g_vCameraDirection;

    // final screen projection
    gl_Position = ProjectToScreen(vPosition.xyz,
                                       g_vCameraPosition, g_mModelViewProj);

    // compute alpha scalar based on LOD; will cross fade the 3D geometry with billboard images
    float fDistance;
    vout_diffuseTexCoords.w = Compute3dFade(fDistance,
                                               c_fAlphaScalar, g_vCameraPosition,
                                               g_vTreePosAndScale, g_vLodProfile);
    vout_normalVec.w = SpecialEffectFade(fDistance, g_vLodProfile);

    // passthrough values
    vout_diffuseTexCoords.xy = c_vDiffuseTexCoords;

    // shadow projection
#ifdef SPEEDTREE_SHADOWS_ENABLED
    for (int i = 0; i < SPEEDTREE_NUM_SHADOW_MAPS; ++i)
        sOut.m_av2dPosInLightSpace[i] = ProjectToLightSpace(vPosition.xyz, g_amLightViewProjs[i]);

    // need the depth to determine which shadow map to use
    vout_detailTexCoords.z = gl_Position.z / g_fFarClip;
#endif

    vout_diffuseTexCoords.z = 0.0;

#ifdef GOOGLE_COMPUTE_FOG
    // compute fog value
    vout_fogData = FogVertex(vPosition.xyz);
#endif
}
