

layout(binding=0, rgba16ui)
#ifdef LOGIC_MODIFIER
  uniform uimage3D outputData;
#else
  uniform writeonly uimage3D outputData;
#endif
  
layout(local_size_x = LOCAL_X_MAX, local_size_y = LOCAL_Y_MAX, local_size_z = LOCAL_Z_MAX) in;



shared bool bit_local [4*4*4];


/////////////////////////////////////////////////////////////////////////////
// main
//

void main()
{

  uvec3
    T = uvec3 (THREADLOAD_X_MAX, THREADLOAD_Y_MAX, THREADLOAD_Z_MAX),
    L = uvec3 (LOCAL_X_MAX,      LOCAL_Y_MAX,      LOCAL_Z_MAX),
    W = uvec3 (GROUP_ONE_X_MAX,  GROUP_ONE_Y_MAX,  GROUP_ONE_Z_MAX);

  uint
    uID = gl_WorkGroupID.x;

  // XYZ group to be extracted from uID

  uint
    uCube = gl_WorkGroupID.y;

  uint
    u1, 
    u2;

  if (uCube % 2 == 0)
  {
    u1 = coordinateSetValue[uCube/2].x;
    u2  = coordinateSetValue[uCube/2].y;
  }
  else
  {
    u1 = coordinateSetValue[uCube/2].z;
    u2  = coordinateSetValue[uCube/2].w;
  }

  /*
    Following bit packing magic detailed and also run in HueVirtualTextureSpaceLeaping.cpp.
  */

  uvec3
    uvecInMin = uvec3 ( (u1 >> 0u) & ((1u << 9u) - 1u), (u1 >> 9u) & ((1u << 9u) - 1u),  (u1 >> (9u + 9u)) & ((1u << 9u) - 1u) );

  uint
    uLOD = (u1 >> (9u + 9u + 9u)) &  ((1u << 4u) - 1u);

  uint
    uEdgeX = (u1 >> (9u + 9u + 9u  + 4u)) & ((1u << 1u) - 1u);
  
  uint
    uEdgeYZ = (u2 >> 30u)  & (( 1u << 2u) -1u);

  uint
    uEdge = uEdgeX | (uEdgeYZ << 1u);

  uvec3
    uvecOutMin = uvec3 ( (u2 >> 0u) & (( 1 << 10u) -1u), (u2 >> 10u)  & (( 1u << 10u) -1u), (u2 >> 20u)  & (( 1u << 10u) - 1u) );

  uvec3                           
    uvecCornerSize = uvec3 (cornerTileSize[uLOD].x, cornerTileSize[uLOD].y, cornerTileSize[uLOD].z); 

  bool
    isEdgeX = (uEdge & 0x01) != 0,
    isEdgeY = (uEdge & 0x02) != 0,
    isEdgeZ = (uEdge & 0x04) != 0;

  uvec3                           
    uvecInSize = uvec3 (isEdgeX ? uvecCornerSize.x : uSubTileSize, isEdgeY ? uvecCornerSize.y : uSubTileSize, isEdgeZ ? uvecCornerSize.z : uSubTileSize);

  uvec3
    cubeInMin = uSubTileSize * uvecInMin,
    cubeInMax = cubeInMin + uvecInSize;

  uvec3
    cubeOutMin = uSpaceLeapingTileSize * uvecOutMin;

  // id => xyz on output space leaping cube
  uint
    uXOut =  uID % W.x,
    uYOut = (uID /W.x) % W.y,
    uZOut = (uID /W.x/ W.y) % W.z;

  uvec3
    w = uvec3 (uXOut, uYOut, uZOut);

  // LINE 346 

  ivec3
    cubeOut = ivec3 (cubeOutMin.x, cubeOutMin.y, cubeOutMin.z);

  uint
    i = gl_LocalInvocationID.x,
  	j = gl_LocalInvocationID.y,
  	k = gl_LocalInvocationID.z;

  uint
    bit_offset = k * 4u * 4u + j * 4u + i;

  bool
    bitCalc = false;

  uvec3
    cR = w;

  cR = cR * L;
  cR = cR * T;
  cR = cR + cubeInMin;

  for (uint tz = 0; tz < T.z; tz++) {
  for (uint ty = 0; ty < T.y; ty++) {
  for (uint tx = 0; tx < T.x; tx++)
  {
    uvec3
      l = uvec3 (i, j, k);

    uvec3
      t = uvec3 (tx, ty, tz);

    bool
      isSequential = false;

    if (isSequential)
    {
      uvec3
        cLTemp = l;

      cLTemp = cLTemp * T;

      cR = cR + cLTemp;

      cR = cR + t;
    }
    else
    {
      uvec3
        cTemp = t;

      cTemp = cTemp * L;

      cR = cR + cTemp;

      cR = cR + l;
    }


    if (cR.x < cubeInMax.x && cR.y < cubeInMax.y && cR.z < cubeInMax.z)
    {
      bool
        isNoValue;

      float
        rRed = GetVoxelFloat(int (cR.x), int (cR.y), int (cR.z), isNoValue);

       bitCalc = (rRed > 0.5f);
    }
  }
 }}


 bit_local[bit_offset] = bitCalc;

 barrier();

if (i == 0 && j == 0 && k == 0)
{
  uint
	vec[4];

  vec[0] = 0;
  vec[1] = 0;
  vec[2] = 0;
  vec[3] = 0;

  for (k = 0u; k < 4u; k++)
  {
	for (j = 0u; j < 4u; j++)
	{
	  for (i = 0u; i < 4u; i++)
	  {
		uint
		  ibitindex = k * 4u * 4u + j * 4u + i;

        if (bit_local[ibitindex])
		{
		  uint
			thisbit = 4u * j + i;

		  uint
			mask = 1u << thisbit;

          vec[k] = vec[k] | mask;
		}
	  }
	}
  }

  uvec4
	thisInvisible = uvec4 (vec[0], vec[1], vec[2], vec[3]);

#ifdef LOGIC_MODIFIER

    uvec4
      readInvisible = imageLoad(outputData, cubeOut);

    uvec4
      readVisible = ~readInvisible,
      thisVisible = ~thisInvisible;
  
    thisVisible = thisVisible LOGIC_MODIFIER readVisible;

    thisInvisible = ~thisVisible;

#endif
	
	imageStore(outputData, cubeOut, thisInvisible);
  }
}

