#version 410 core

layout(vertices = ELEMENT_COUNT) out;
in vec3 vert_CS_in[];
out vec3 vert_ES_in[];

uniform mat4 uModelViewProjectionMatrix;

uniform float uTessPixelFactor;
uniform float uProjInfoY;

bool IsOutside(vec3 cMin, vec3 cMax)
{
  int
    uAndMask = 0xff;

  for (int i=0; i<8; i++)
  {
    vec3
      cPos = cMin;

    if ((i&1) == 1) cPos.x = cMax.x;
    if ((i&2) == 2) cPos.y = cMax.y;
    if ((i&4) == 4) cPos.z = cMax.z;

    // transformed pos
    vec4 cTPos = uModelViewProjectionMatrix * vec4(cPos.xyz, 1.0);

    int
      uMask = 0;

    if (cTPos.w < 0.0000001) uMask = 16;
    else
    {
      cTPos.x /= cTPos.w;
      cTPos.y /= cTPos.w;

      if (cTPos.x < -1) uMask |= 1;
      else if (cTPos.x > 1) uMask |= 2;
      if (cTPos.y < -1) uMask |= 4;
      else if (cTPos.y > 1) uMask |= 8;
    }

    uAndMask &= uMask;
  }

  if (uAndMask > 0) return true;
  else return false;
}

float
CalcTessSize(vec3 cPosIn, float rRadius)
{
  float
    rDist = uModelViewProjectionMatrix[0][3] * cPosIn.x +
            uModelViewProjectionMatrix[1][3] * cPosIn.y + 
            uModelViewProjectionMatrix[2][3] * cPosIn.z + 
            uModelViewProjectionMatrix[3][3];

  float
    rPixelUnit = uProjInfoY * -rDist;

  float rPixels = rRadius / rPixelUnit;

  if (rPixels < 1.0) rPixels = 1.0;
  else if (rPixels > 7.0) rPixels = 7.0;
 
  return rPixels;
}

float
CalcTessSize(vec3 cPosA, vec3 cPosB)
{
  vec3
    cCenter = (cPosA + cPosB) / 2.0;

  float
    rRadius = length(cPosA-cPosB);

  float
    rDist = uModelViewProjectionMatrix[0][3] * cCenter.x +
            uModelViewProjectionMatrix[1][3] * cCenter.y + 
            uModelViewProjectionMatrix[2][3] * cCenter.z + 
            uModelViewProjectionMatrix[3][3];

  if (rDist <= 0.0) return 1024.0;

  float
    rPixelUnit = uTessPixelFactor * uProjInfoY * -rDist;

  float rPixels = rRadius / rPixelUnit;

  return rPixels;
}

void main(void)
{

  if (gl_InvocationID == 0)
  {
    if (IsOutside(vert_CS_in[ELEMENT_COUNT], vert_CS_in[ELEMENT_COUNT+1] ))
    {
      gl_TessLevelOuter[0] = 0.0;
      gl_TessLevelOuter[1] = 0.0;
      gl_TessLevelOuter[2] = 0.0;
      gl_TessLevelOuter[3] = 0.0;
      gl_TessLevelInner[0] = 0.0;
      gl_TessLevelInner[1] = 0.0;
    }
    else
    {
      gl_TessLevelOuter[1] = CalcTessSize(vert_CS_in[0], vert_CS_in[1]);
      gl_TessLevelOuter[3] = CalcTessSize(vert_CS_in[U_SIZE], vert_CS_in[U_SIZE + 1]);
      gl_TessLevelOuter[0] = CalcTessSize(vert_CS_in[0], vert_CS_in[U_SIZE]);
      gl_TessLevelOuter[2] = CalcTessSize(vert_CS_in[1], vert_CS_in[U_SIZE + 1]);
      gl_TessLevelInner[0] = (gl_TessLevelOuter[1] + gl_TessLevelOuter[3]) / 2.0;
      gl_TessLevelInner[1] = (gl_TessLevelOuter[0] + gl_TessLevelOuter[2]) / 2.0;
    }
  }
  vert_ES_in[gl_InvocationID] = vert_CS_in[gl_InvocationID];
}