// The shader writes a uniform parameter to the inner and edge tessellation factors.

gp5tcp PATCH_16
void main
(
  in int ndx                      : CONTROLPOINT_ID,
  in AttribArray<float3> position : POSITION,
  in AttribArray<float2> texCoord : TEXCOORD0,

  out float4 oPosition     : POSITION,
  out float2 oTexCoord     : TEXCOORD0,
  out float  oEdgeTess[4]  : EDGETESS,
  out float  oInnerTess[2] : INNERTESS,

  uniform float2   innerTess,
  uniform float4   outerTess,

  uniform float4x4 modelView
)
{
  oPosition = float4(position[ndx], 1);
  oPosition = mul(modelView,oPosition);
  oTexCoord = texCoord[ndx];

  oEdgeTess[0] = outerTess.x;
  oEdgeTess[1] = outerTess.y;
  oEdgeTess[2] = outerTess.z;
  oEdgeTess[3] = outerTess.w;

  oInnerTess[0] = innerTess.x;
  oInnerTess[1] = innerTess.y;
}

gp5tep PATCH_16 QUADS ORDER_CCW
void main
(
  in float2 uv                    : UV,
  in AttribArray<float3> position : POSITION,
  in AttribArray<float2> texcoord : TEXCOORD0,

  out float4 oPosition : POSITION,
  out float3 oNormal   : TEXCOORD1,
  out float2 oUV       : TEXCOORD2,
  out float3 oGradU    : TEXCOORD3,
  out float3 oGradV    : TEXCOORD4,
  out float4 oTexCoord : TEXCOORD5,

  uniform float4x4 projection
)
{
  // Compute Bernstein weights for U (1 ADD, 8 MULs)
  float  u          = uv.x;
  float  uu         = u*u;
  float  oneMinusU  = 1-u;
  float  oneMinusUU = oneMinusU*oneMinusU;
  float3 Bu[4] =
  {
    (oneMinusU*oneMinusUU).xxx, // (1-u)^3
    (3*u*oneMinusUU).xxx,       // 3u(1-u)^2
    (3*uu*oneMinusU).xxx,       // 3u^2(1-u)
    (u*uu).xxx                  // u^3
  };
  float3 dBu[4] =
  {
    (-3*oneMinusUU).xxx,                 // -3(1-u)^2
    (-6*u*oneMinusU + 3*oneMinusUU).xxx, // -6u(1-u)+3(1-u)^2
    (-3*uu + 6*u*oneMinusU).xxx,         // -3u^2+6u(1-u)
    (3*uu).xxx                           // 3u^2
  };

  // Compute Bernstein weights for V (1 ADD, 8 MULs)
  float  v          = uv.y;
  float  vv         = v*v;
  float  oneMinusV  = 1-v;
  float  oneMinusVV = oneMinusV*oneMinusV;
  float3 Bv[4] =
  {
    (oneMinusV*oneMinusVV).xxx, // (1-v)^3
    (3*v*oneMinusVV).xxx,       // 3v(1-v)^2
    (3*vv*oneMinusV).xxx,       // 3v^2(1-v)
    (v*vv).xxx                  // v^3
  };
  float3 dBv[4] =
  {
    (-3*oneMinusVV).xxx,                 // -3(1-v)^2
    (-6*v*oneMinusV + 3*oneMinusVV).xxx, // -6v(1-v)+3(1-v)^2
    (-3*vv + 6*v*oneMinusV).xxx,         // -3v^2+6v(1-v)
    (3*vv).xxx                           // 3v^2
  };

  // Four horizontally symmetric sum-of-products with Bu weights (3*8 MULs, 3*8 MADs, 3*4 ADDs)
  [precise] float3 w0 = fma(Bu[0], position[0], Bu[1]*position[4]) + fma(Bu[3], position[12], Bu[2]*position[8]),
                   w1 = fma(Bu[0], position[1], Bu[1]*position[5]) + fma(Bu[3], position[13], Bu[2]*position[9]),
                   w2 = fma(Bu[0], position[2], Bu[1]*position[6]) + fma(Bu[3], position[14], Bu[2]*position[10]),
                   w3 = fma(Bu[0], position[3], Bu[1]*position[7]) + fma(Bu[3], position[15], Bu[2]*position[11]);

  oPosition = float4(fma(Bv[0],w0,Bv[1]*w1) + fma(Bv[3],w3,Bv[2]*w2),1);

  // Compute gradients in U and V

  [precise] float3 uw0 = fma(dBu[0], position[0], dBu[1]*position[4]) + fma(dBu[3], position[12], dBu[2]*position[8]),
                   uw1 = fma(dBu[0], position[1], dBu[1]*position[5]) + fma(dBu[3], position[13], dBu[2]*position[9]),
                   uw2 = fma(dBu[0], position[2], dBu[1]*position[6]) + fma(dBu[3], position[14], dBu[2]*position[10]),
                   uw3 = fma(dBu[0], position[3], dBu[1]*position[7]) + fma(dBu[3], position[15], dBu[2]*position[11]);

  oGradU = fma(Bv[0],uw0,Bv[1]*uw1) + fma(Bv[3],uw3,Bv[2]*uw2);

  [precise] float3 vw0 = fma(dBv[0], position[0], dBv[1]*position[1]) + fma(dBv[3], position[3], dBv[2]*position[2]),
                   vw1 = fma(dBv[0], position[4], dBv[1]*position[5]) + fma(dBv[3], position[7], dBv[2]*position[6]),
                   vw2 = fma(dBv[0], position[8], dBv[1]*position[9]) + fma(dBv[3], position[11], dBv[2]*position[10]),
                   vw3 = fma(dBv[0], position[12], dBv[1]*position[13]) + fma(dBv[3], position[15], dBv[2]*position[14]);

  oGradV = fma(Bu[0],vw0,Bu[1]*vw1) + fma(Bu[3],vw3,Bu[2]*vw2);

  oTexCoord = float4(1,1,0,0);

  oUV = uv;
  oPosition = mul(projection,oPosition);
  oNormal = cross(oGradV,oGradU);
}

float3 visualizeUV(float2 uv)
{
  // [0,0] black
  // [1,0] red
  // [0,1] green
  // [1,1] yellow

  float uvSum = uv.x + uv.y;
  float uvMax = max(uv.x,uv.y);
  float t     = saturate((uvSum-1)/2);
  return lerp(float3(uv,0),float3(uvMax.xx,0),t);
}

float3 visualizeTangent(float3 v)
{
  return abs(normalize(v));
}

////////////////////////////////////////////

void vertexTransform
(
  in float4 position : POSITION,
  in float4 normal   : NORMAL,

  out float4 oPosition : POSITION,
  out float4 oNormal   : TEXCOORD1,

  uniform float4x4 modelView,
  uniform float4x4 projection
)
{
  oPosition = position;
  oPosition = mul(projection,oPosition);
  oPosition = mul(modelView,oPosition);
  oNormal = normal;
}

float3 fragmentUV      (float2 uv     : TEXCOORD2) : COLOR { return visualizeUV(uv); }
float3 fragmentTangentU(float3 gradU  : TEXCOORD3) : COLOR { return visualizeTangent(gradU); }
float3 fragmentTangentV(float3 gradV  : TEXCOORD4) : COLOR { return visualizeTangent(gradV); }
float3 fragmentNormal  (float3 normal : TEXCOORD1) : COLOR { return visualizeTangent(normal); }

float3 fragmentTextureUV
(
  uniform sampler2D texture : TEX0,
  float2 uv                 : TEXCOORD2
) : COLOR
{
  return tex2D(texture,uv);
}

float4 fragmentFlat
(
  uniform float4 color
) : COLOR
{
  return color;
}

float3 fragmentLighting
(
  float4 position : TEXCOORD0,
  float3 normal   : TEXCOORD1,

  uniform float3 Kd

) : COLOR
{
  float3 globalAmbient = float3(0.1,0.1,0.1);
  float3 lightColor    = float3(0.95, 0.95, 0.95);
  float3 lightPosition = float3(2,2,2);
  float3 eyePosition   = float3(0,0,1);
  float3 Ke            = float3(0,0,0);
  float3 Ka            = float3(0,0,0);
  float3 Ks            = float3(0.7, 0.6, 0.6);
  float  shininess     = 32.0;

  float3 P = position.xyz;
  float3 N = normalize(normal);

  // Compute emissive term
  float3 emissive = Ke;

  // Compute ambient term
  float3 ambient = Ka * globalAmbient;

  // Compute the diffuse term
  float3 L = normalize(lightPosition - P);
  float diffuseLight = max(dot(L, N), 0);
  float3 diffuse = Kd * lightColor * diffuseLight;

  // Compute the specular term
  float3 V = normalize(eyePosition - P);
  float3 H = normalize(L + V);
  float specularLight = pow(max(dot(H, N), 0), shininess);
  if (diffuseLight <= 0) specularLight = 0;
  float3 specular = Ks * lightColor * specularLight;

  float3 color = emissive + ambient + diffuse + specular;
  return color;
}

float4   outerTess;
float2   innerTess;
float4x4 modelView;
float4x4 projection;

float4   diffuse   = float4(0.5,0,0,1);
float4   highlight = float4(1,1,1,1);

sampler2D texture = sampler_state {
  minFilter = Linear;
  magFilter = Linear;
};

technique Lighting
{
    pass <string geometry = "drawBezierPatch4x4";>
    {
        TessellationControlProgram    = compile gp5tcp main(innerTess, outerTess, modelView);
        TessellationEvaluationProgram = compile gp5tep main(projection);
        FragmentProgram               = compile gp4fp  fragmentLighting(diffuse);
    }
}

technique ControlPoints
{
    pass <string geometry = "drawBezierPatch4x4Points";>
    {
        PointSize = 3;
        DepthTestEnable = false;
        VertexProgram                 = compile gp4vp vertexTransform(projection,modelView);
        FragmentProgram               = compile gp4fp fragmentFlat(highlight);
    }

    pass <string geometry = "drawBezierPatch4x4LineLoop";>
    {
        VertexProgram                 = compile gp4vp vertexTransform(projection,modelView);
        FragmentProgram               = compile gp4fp fragmentFlat(highlight);
    }
}

technique All
{
    pass <string geometry = "drawBezierPatch4x4";>
    {
        TessellationControlProgram    = compile gp5tcp main(innerTess, outerTess, modelView);
        TessellationEvaluationProgram = compile gp5tep main(projection);
        FragmentProgram               = compile gp4fp  fragmentLighting(diffuse);
    }

    pass <string geometry = "drawBezierPatch4x4Points";>
    {
        PointSize = 3;
        DepthTestEnable = false;
        VertexProgram                 = compile gp4vp vertexTransform(projection,modelView);
        FragmentProgram               = compile gp4fp fragmentFlat(highlight);
    }

    pass <string geometry = "drawBezierPatch4x4LineLoop";>
    {
        VertexProgram                 = compile gp4vp vertexTransform(projection,modelView);
        FragmentProgram               = compile gp4fp fragmentFlat(highlight);
    }
}

technique TextureUV
{
    pass <string geometry = "drawBezierPatch4x4";>
    {
        TessellationControlProgram    = compile gp5tcp main(innerTess, outerTess, modelView);
        TessellationEvaluationProgram = compile gp5tep main(projection);
        FragmentProgram               = compile gp4fp  fragmentTextureUV(texture);
    }
}

technique Wireframe
{
    pass <string geometry = "drawBezierPatch4x4";>
    {
        PolygonMode = { FrontAndBack, Line };
        TessellationControlProgram    = compile gp5tcp main(innerTess, outerTess, modelView);
        TessellationEvaluationProgram = compile gp5tep main(projection);
        FragmentProgram               = compile gp4fp  fragmentFlat(diffuse);
    }
}

technique UV
{
    pass <string geometry = "drawBezierPatch4x4";>
    {
        TessellationControlProgram    = compile gp5tcp main(innerTess, outerTess, modelView);
        TessellationEvaluationProgram = compile gp5tep main(projection);
        FragmentProgram               = compile gp4fp  fragmentUV();
    }
}

technique TangentU
{
    pass <string geometry = "drawBezierPatch4x4";>
    {
        TessellationControlProgram    = compile gp5tcp main(innerTess, outerTess, modelView);
        TessellationEvaluationProgram = compile gp5tep main(projection);
        FragmentProgram               = compile gp4fp  fragmentTangentU();
    }
}

technique TangentV
{
    pass <string geometry = "drawBezierPatch4x4";>
    {
        TessellationControlProgram    = compile gp5tcp main(innerTess, outerTess, modelView);
        TessellationEvaluationProgram = compile gp5tep main(projection);
        FragmentProgram               = compile gp4fp  fragmentTangentV();
    }
}

technique Normal
{
    pass <string geometry = "drawBezierPatch4x4";>
    {
        TessellationControlProgram    = compile gp5tcp main(innerTess, outerTess, modelView);
        TessellationEvaluationProgram = compile gp5tep main(projection);
        FragmentProgram               = compile gp4fp  fragmentNormal();
    }
}
