/***************************************************************************
 *                          Zanoza Modeler v3.x                            *
 *      This is unpublished propietary source code of Zanoza Software.     *
 *      The copyright notice does not evidence any actual or intended      *
 *                    publication of such source code.                     *
 *                                                                         *
 *                 Copyright (c) 2002-2012 Zanoza Software.                *
 *                                                                         *
 *                        All rights reserved.                             *
 ***************************************************************************
 *--------------------------------------------------------------------------
 * @author:   Oleg M.
 *--------------------------------------------------------------------------
 * @purpose:  ZModeler 3.x shaders common constants and routines
 *--------------------------------------------------------------------------
 * @histroy: 12.09.2011. Created
<[[
Version  = 1.9
]]>
 */
#ifndef ANIM_PALETTE_SIZE
#define ANIM_PALETTE_SIZE     100
#endif

#define ADVANCED_COLOR_DIFFUSELEVEL(_v) float4 _v = float4( \
	g_diffuse2Color.a*(DIFFUSECOLOR2_INPUT_MASK), \
	g_diffuse3Color.a*(DIFFUSECOLOR3_INPUT_MASK), \
	g_diffuse4Color.a*(DIFFUSECOLOR4_INPUT_MASK), 0);

#define ADVANCED_COLOR_DIFFUSEVAR(_v, _a) float3 _v = ((g_colors[1].rgb*(1 - _a.r) + g_diffuse2Color.rgb*_a.r)*(1 - _a.g) + g_diffuse3Color.rgb*_a.g)*(1 - _a.b) + g_diffuse4Color.rgb*_a.b;

#define ADVANCED_COLOR_EFFECTLEVELS(_v) float4 _v = float4(g_dustColor.a*g_effectLevels.r*(DUSTCOLOR_INPUT_MASK), \
	g_scratchColor.a*g_effectLevels.g*(SCRATCHCOLOR_INPUT_MASK), \
	g_dirtColor.a*g_effectLevels.b*(DIRTCOLOR_INPUT_MASK), \
	g_burnColor.a*g_effectLevels.a*(BURNCOLOR_INPUT_MASK));

#define ADVANCED_COLOR_EFFECTVAR(_v, _e) float4 _v = float4(((g_dustColor.rgb*_e.r*(1-_e.g) + \
	g_scratchColor.rgb*_e.g)*(1-_e.b) + \
	g_dirtColor.rgb*_e.b)*(1-_e.a) + g_burnColor.rgb*_e.a, \
	saturate(dot(_e.rgba, 1)));

#define ADVANCED_COLOR_OPTIONS(_v, _f, _a) float4 _v = (float4)0; \
	_v.r = saturate(1-dot(_f, float4(DUSTCOLOR_MATTED, SCRATCHCOLOR_MATTED, DIRTCOLOR_MATTED, BURNCOLOR_MATTED)) - \
			dot(_a, float4(DIFFUSECOLOR2_MATTED, DIFFUSECOLOR3_MATTED, DIFFUSECOLOR4_MATTED, 0))); \
	_v.g = dot(_f, float4(DUSTCOLOR_GLOSSY, SCRATCHCOLOR_GLOSSY, DIRTCOLOR_GLOSSY, BURNCOLOR_GLOSSY)) + \
			dot(_a, float4(DIFFUSECOLOR2_GLOSSY, DIFFUSECOLOR3_GLOSSY, DIFFUSECOLOR4_GLOSSY, 0)); \
	_v.b = dot(_f, float4(DUSTCOLOR_OPAQUE, SCRATCHCOLOR_OPAQUE, DIRTCOLOR_OPAQUE, BURNCOLOR_OPAQUE)) + \
			dot(_a, float4(DIFFUSECOLOR2_OPAQUE, DIFFUSECOLOR3_OPAQUE, DIFFUSECOLOR4_OPAQUE, 4));


//----------------------------------------------------------------------
// common set of globals constants:
//----------------------------------------------------------------------
// c0-c3        : world;
// c4-c7        : view matrix;
// c8-c11       : projection matrix;
// c12-c15      : world-view-projection matrix;
//
// c12-c15       : ambient RGB, diffuse RGB + A:opacity, specular RGB + A:strength, emissive RGB + A:strength;
//----------------------------------------------------------------------
float4x4    g_mWorld         : register(c0);
float4x4    g_mView          : register(c4);
float4x4    g_mProj          : register(c8);
float4x4    g_mWorldViewProj : register(c12);
float4      g_colors[4]      : register(c16);
float4      g_viewDirection  : register(c20);
float4      g_viewPosition   : register(c21);   //*,*,*,0 on non-perspective views;
float4      g_lightDir       : register(c22);
float4      g_lightColor     : register(c23);
float4      g_ambientLight   : register(c24);
float4      g_fog            : register(c25);   //[r,g,b, distance to 100% level or 0 to disable;]
float4      g_deform         : register(c26);   //[deform level VS, transform level, vertcolor usage, deform level PS]
float4      g_effectLevels   : register(c27);   //[dust level, scratch level, dirt level, burn level]
float2x4    g_animQuat[ANIM_PALETTE_SIZE] : register(c55);  //dual-quad animation palette for upto 100 entries;

#ifdef VERTEX_SHADER
//
// converts dual quaterion to transformation matrix;
//
float4x4 matrixFromDualQuat(float2x4 dual)
{
  float4x4 m ;
  float length = dot(dual[0], dual[0]);
  float x = dual[0].x, y = dual[0].y, z = dual[0].z, w = dual[0].w;
  float t1 = dual[1].x, t2 = dual[1].y, t3 = dual[1].z, t0 = dual[1].w;

  float4x4 mTransform = {  //row 0
                           w*w + x*x - y*y - z*z,
                           2*x*y + 2*w*z,
                           2*x*z - 2*w*y,
                           0,
                           //row 1
                           2*x*y - 2*w*z,
                           w*w + y*y - x*x - z*z,
                           2*y*z + 2*w*x,
                           0,
                           //row 2
                           2*x*z + 2*w*y,
                           2*y*z - 2*w*x,
                           w*w + z*z - x*x - y*y,
                           0,
                           //row 3
                           -2*t0*x + 2*t1*w - 2*t2*z + 2*t3*y,
                           -2*t0*y + 2*t1*z + 2*t2*w - 2*t3*x,
                           -2*t0*z - 2*t1*y + 2*t2*x + 2*t3*w, 
                           1};
     
    
  return mTransform/length;
}
//
// common vertex transformation function. 
// respects deformation and transformation tweening;
//

float4 transformInput(in VS_INPUT v, inout float4 vNorm, inout float4 vTangent, inout float4 vView) : POSITION
{
  float4x4 mTransform = (float4x4)0;
  if (g_deform.y > 0.01)
  {
    float2x4 finalDualquat = (float2x4)0;
    float2x4 m;
    finalDualquat[0].w = 1;
    finalDualquat[1].w = 1;
    float4 weights0 = {v.vWeights0.x*g_deform.y, v.vWeights0.y*g_deform.y, v.vWeights0.z*g_deform.y, v.vWeights0.w*g_deform.y};
    float4 weights1 = {v.vWeights1.x*g_deform.y, v.vWeights1.y*g_deform.y, v.vWeights1.z*g_deform.y, 
                        (1 - v.vWeights0.x - v.vWeights0.y - v.vWeights0.z - v.vWeights0.w - v.vWeights1.x - v.vWeights1.y - v.vWeights1.z)*g_deform.y};

    m = g_animQuat[v.vBones0.x];
    float4 dq0 = (float1x4)m;

    finalDualquat = weights0.x*m;

    m = g_animQuat[v.vBones0.y];
    float4 dq = (float1x4)m;
    if (dot(dq0, dq) < 0)
      finalDualquat -= weights0.y*m;
    else
      finalDualquat += weights0.y*m;

    m = g_animQuat[v.vBones0.z];
    dq = (float1x4)m;
    if (dot(dq0, dq) < 0)
      finalDualquat -= weights0.z*m;
    else
      finalDualquat += weights0.z*m;

    m = g_animQuat[v.vBones0.w];
    dq = (float1x4)m;
    if (dot(dq0, dq) < 0)
      finalDualquat -= weights0.w*m;
    else
      finalDualquat += weights0.w*m;

    m = g_animQuat[v.vBones1.x];
    dq = (float1x4)m;
    if (dot(dq0, dq) < 0)
      finalDualquat -= weights1.x*m;
    else
      finalDualquat += weights1.x*m;

    m = g_animQuat[v.vBones1.y];
    dq = (float1x4)m;
    if (dot(dq0, dq) < 0)
      finalDualquat -= weights1.y*m;
    else
      finalDualquat += weights1.y*m;

    m = g_animQuat[v.vBones1.z];
    dq = (float1x4)m;
    if (dot(dq0, dq) < 0)
      finalDualquat -= weights1.z*m;
    else
      finalDualquat += weights1.z*m;

    m = g_animQuat[v.vBones1.w];
    dq = (float1x4)m;
    if (dot(dq0, dq) < 0)
      finalDualquat -= weights1.w*m;
    else
      finalDualquat += weights1.w*m;

    mTransform = matrixFromDualQuat(finalDualquat);
  }
  else
    mTransform[0][0] = mTransform[1][1] = mTransform[2][2] = mTransform[3][3] = 1;
    

  // [optionally] deformed position:
  float4 vPosDef = v.vPosition + g_deform.x*float4(v.vDeformPosition.xyz,0);
  // [optionally] transformed position:
  vPosDef = float4(mul(float4(mul(vPosDef, (float4x3)mTransform),1), (float4x3)g_mWorld),1);

  vView = normalize(g_viewPosition - vPosDef);
  // position in camera space:
  vPosDef = float4(mul(vPosDef, (float4x3)g_mView),1);
  // position on screen:
  vPosDef = mul(vPosDef, g_mProj);

  // [optionally] deformed normal:
  vNorm = vNorm + g_deform.x*any(v.vDeformNormal.xyz)*float4(v.vDeformNormal.xyz*4-(float3)(2,2,2), 0);
  // transformed normal:
  vNorm.xyz = mul(mul(vNorm.xyz, (float3x3)mTransform), (float3x3)g_mWorld);
  // transformed tangent:
  vTangent = float4(mul(mul(vTangent.xyz, (float3x3)mTransform), (float3x3)g_mWorld), vTangent.w);

  return vPosDef;
}

#endif //VERTEX_SHADER
