#version 450

#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable

//layout(triangles, fractional_even_spacing, ccw) in;
layout(triangles, fractional_odd_spacing, ccw) in;


struct pn_patch
{
  float b210;
  float b120;
  float b021;
  float b012;
  float b102;
  float b201;
  float b111;
  float n110;
  float n011;
  float n101;
};


layout(location = 0) in vec4 tcs_normal[gl_MaxPatchVertices];
layout(location = 3) in vec4 tcs_uv[gl_MaxPatchVertices];
layout(location = 6) in pn_patch tcs_patch[gl_MaxPatchVertices];

layout(location = 0) out vec4 tes_normal;
layout(location = 1) out vec4 tes_uv;
layout(location = 2) out vec4 v_eye_dir;
layout(location = 3) out vec4 v_light_dir;


layout (binding = 3) uniform sampler2D tex_bump;


layout (std140, binding = 0) uniform uniforms_t
{ 
  mat4 ProjectionMatrix;
  mat4 ViewMatrix;
  mat4 ModelMatrix;
  vec4 uv_tiling;
  vec4 light_position;
  vec4 tess_params;
} ub;


#define uvw gl_TessCoord


void main()
{

  vec3 uvwSquared = uvw * uvw;
  vec3 uvwCubed   = uvwSquared * uvw;

  // extract control points
  vec3 b210 = vec3(tcs_patch[0].b210, tcs_patch[1].b210, tcs_patch[2].b210);
  vec3 b120 = vec3(tcs_patch[0].b120, tcs_patch[1].b120, tcs_patch[2].b120);
  vec3 b021 = vec3(tcs_patch[0].b021, tcs_patch[1].b021, tcs_patch[2].b021);
  vec3 b012 = vec3(tcs_patch[0].b012, tcs_patch[1].b012, tcs_patch[2].b012);
  vec3 b102 = vec3(tcs_patch[0].b102, tcs_patch[1].b102, tcs_patch[2].b102);
  vec3 b201 = vec3(tcs_patch[0].b201, tcs_patch[1].b201, tcs_patch[2].b201);
  vec3 b111 = vec3(tcs_patch[0].b111, tcs_patch[1].b111, tcs_patch[2].b111);

  // extract control normals
  vec3 n110 = normalize(vec3(tcs_patch[0].n110, tcs_patch[1].n110, tcs_patch[2].n110));
  vec3 n011 = normalize(vec3(tcs_patch[0].n011, tcs_patch[1].n011, tcs_patch[2].n011));
  vec3 n101 = normalize(vec3(tcs_patch[0].n101, tcs_patch[1].n101, tcs_patch[2].n101));

  // compute texcoords
  vec4 uv  = (gl_TessCoord[2]*tcs_uv[0] + gl_TessCoord[0]*tcs_uv[1] + gl_TessCoord[1]*tcs_uv[2])  * ub.uv_tiling;
  tes_uv = uv;
  
  float bump = texture(tex_bump, uv.xy).x;
  
  
  // normal
  // Barycentric normal
  vec3 barNormal = gl_TessCoord[2]*tcs_normal[0].xyz + gl_TessCoord[0]*tcs_normal[1].xyz + gl_TessCoord[1]*tcs_normal[2].xyz;
  vec3 pnNormal  = tcs_normal[0].xyz*uvwSquared[2] + tcs_normal[1].xyz*uvwSquared[0] + tcs_normal[2].xyz*uvwSquared[1]
                            + n110*uvw[2]*uvw[0] + n011*uvw[0]*uvw[1]+ n101*uvw[2]*uvw[1];
                            
  float tess_alpha = ub.tess_params.z;
  
  vec3 Normal = normalize(tess_alpha*pnNormal + (1.0-tess_alpha) * barNormal);
  

  // compute interpolated pos
  vec3 barPos = gl_TessCoord[2]*gl_in[0].gl_Position.xyz + gl_TessCoord[0]*gl_in[1].gl_Position.xyz + gl_TessCoord[1]*gl_in[2].gl_Position.xyz;

  // save some computations
  uvwSquared *= 3.0;

  // compute PN position
  vec3 pnPos  = gl_in[0].gl_Position.xyz*uvwCubed[2]
                    + gl_in[1].gl_Position.xyz*uvwCubed[0]
                    + gl_in[2].gl_Position.xyz*uvwCubed[1]
                    + b210*uvwSquared[2]*uvw[0]
                    + b120*uvwSquared[0]*uvw[2]
                    + b201*uvwSquared[2]*uvw[1]
                    + b021*uvwSquared[0]*uvw[1]
                    + b102*uvwSquared[1]*uvw[2]
                    + b012*uvwSquared[1]*uvw[0]
                    + b111*6.0*uvw[0]*uvw[1]*uvw[2];


  // final position and normal
  vec3 finalPos = tess_alpha*pnPos + (1.0-tess_alpha)*barPos;
  
  float bump_scale = ub.tess_params.w;
  finalPos += bump * Normal * bump_scale;

  mat4 modelview = ub.ViewMatrix * ub.ModelMatrix;
  
  tes_normal = modelview * vec4(Normal, 0.0);
  
  vec4 view_pos = modelview * vec4(finalPos,1.0);
  v_eye_dir = -view_pos;
  vec4 lp = ub.ViewMatrix * ub.light_position;
  v_light_dir = lp - view_pos;  
  v_light_dir.y *= -1.0;  

  gl_Position = ub.ProjectionMatrix * view_pos;
  gl_Position.y = -gl_Position.y;
  gl_Position.z = (gl_Position.z + gl_Position.w) / 2.0;
}
