#version 430

layout(binding=0) uniform sampler2D tex;

in vec4 posG;
in vec4 prevPosG;
in vec3 normalG;
in float axisG;

in float triangleIdG;

layout(location = 0) out vec4 frag;
// layout(location = 1) out vec4 frag2;

#define PI 3.1415926

uniform float g_time;


uniform mat4 modelViewMatrix;
uniform mat4 modelViewInvMatrix;
uniform mat4 viewInvMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 projectionInvMatrix;

uniform float g_windowWidth = 1280.0;
uniform float g_windowHeight = 720.0;

vec4 rotateXY(vec4 p, float a) {
  vec4 r = p;
  r.x = cos(a)*p.x - sin(a)*p.y;
  r.y = sin(a)*p.x + cos(a)*p.y;
  return r;
}

vec3 rotateXY3(vec3 p, float a) {
  return rotateXY(vec4(p, 0.0), a).xyz;
}

// google glsl rand gave this, thanks and credit flies to
// http://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

const float zFar = 1.0;
const float zNear = 0.0;

float getPointDist(float z) {
    float clipA = zFar / (zFar - zNear);
    float clipB = zFar*zNear / (zNear - zFar);
    return clipB/(z-clipA);
}
float getPointZ(float d) {
    float clipA = zFar / (zFar - zNear);
    float clipB = zFar*zNear / (zNear - zFar);
    return (clipB + d*clipA)/d;
}

vec4 CalcEyeFromWindow(in vec3 windowSpace) {
    vec3 ndcPos;
    vec4 viewport = vec4(0.0, 0.0, g_windowWidth, g_windowHeight);
    ndcPos.xy = ((2.0 * windowSpace.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1;
//    ndcPos.z = (2.0 * windowSpace.z - zNear - zFar) / (zFar - zNear);
    ndcPos.z = (2.0 * windowSpace.z - zNear - zFar) / (zFar - zNear);
    vec4 clipPos;
    clipPos.w = projectionMatrix[3][2]/(ndcPos.z-(projectionMatrix[2][2]/projectionMatrix[2][3]));
    clipPos.xyz = ndcPos * clipPos.w;
    return projectionInvMatrix * clipPos;
}

vec2 getBump(vec2 uv) {
    vec2 d = vec2(0.01, 0.0);
    return vec2(texture2D(tex, uv+d.xy).g-texture2D(tex, uv-d.xy).g,
                texture2D(tex, uv+d.yx).g-texture2D(tex, uv-d.yx).g);
}


float atanSafe(float y, float x) {
    float ret=0.0;
    if (x!=0.0) {
        if (x>0.0) {
            ret=atan(y/x);
        } else	{
            ret=atan(y/x)+3.141592;
        }
    } else {
        if (y>=0.0) {
            ret=0.5*3.141592;
        } else {
            ret=-0.5*3.141592;
        }
    }
    return ret;
}

// from pouet raymarching thread by las of mercury
float perlin(vec3 p) {
    vec3 i = floor(p);
    vec4 a = dot(i, vec3(1., 57., 21.)) + vec4(0., 57., 21., 78.);
    vec3 f = cos((p-i)*3.14159265)*(-.5)+.5;
    a = mix(sin(cos(a)*a),sin(cos(1.+a)*(1.+a)), f.x);
    a.xy = mix(a.xz, a.yw, f.y);
    return mix(a.x, a.y, f.z);
}

float turb(vec3 c) {
        float r=0.0;
        float s=0.5;
        c*=1.0;
        for (int i=0;i<8;i++) {
                //if (i<2) continue;
                r+=s*(perlin(c)*1.0+0.0);
                c*=3.0;
                s*=0.55;
        }
        return r;
}


layout(binding=0, rgba32f) uniform image2D voxel;
layout(binding=1, r32i) uniform iimage2D voxelHP;
layout(binding=2, r32i) uniform iimage2D voxelNP;
layout(binding=3, rgba32f) uniform image2D voxelPos;
layout(binding=4, rgba32f) uniform image2D voxelNor;

layout(binding=0, offset=0) uniform atomic_uint ac;

uniform float g_voxelDim;

void main() {

  vec3 normal = normalG;
  vec4 diffuse = vec4(1.0);

  // diffuse.rgb = normal.rgb;

  vec3 mp = posG.xyz;
  vec3 pmp = prevPosG.xyz;

  if (axisG < 0.5) {
      mp.zyx = posG.xyz;
      pmp.zyx = prevPosG.xyz;
  } else if (axisG < 1.5) {
      mp.zxy = posG.xyz;
      pmp.zxy = prevPosG.xyz;
  } else {
      mp = posG.xyz;
      pmp = prevPosG.xyz;
  }

  // calculate the 3d voxel point based on the coords

  float vd2 = g_voxelDim*g_voxelDim;
  int vd3 = int(vd2*g_voxelDim);
  vec3 cell = mp*0.5+vec3(0.5, 0.5, 0.5);
  cell = floor(cell*vd2);
  float cellzy = floor(cell.z/g_voxelDim);
  float cellzx = cell.z-cellzy*g_voxelDim;
  ivec2 cellUV = ivec2(cell.x+cellzx*vd2, cell.y+cellzy*vd2);

  imageStore(voxel, cellUV, vec4(1.0));

  //imageAtomicAdd(voxelCount, cellUV, 1);

  normal = normalize(normal);

//  imageAtomicAdd(voxelNX, cellUV, int(normal.x*65535.0));
//  imageAtomicAdd(voxelNY, cellUV, int(normal.y*65535.0));
//  imageAtomicAdd(voxelNZ, cellUV, int(normal.z*65535.0));

  vec3 vel = (mp-pmp)*200.0;

  mp *= 0.50;
  mp += vec3(0.5);
  mp *= vd2;
  mp = fract(mp);


  uint counter = int(atomicCounterIncrement(ac))+1;
  uint cy = counter/(vd3);
  uint cx = counter-uint(cy*vd3);
  ivec2 listCoord = ivec2(cx,cy);

  int hp = imageAtomicExchange(voxelHP, cellUV, int(counter));
  imageStore(voxelNP, listCoord, ivec4(hp));

  imageStore(voxelPos, listCoord, vec4(mp, 0.0));
  imageStore(voxelNor, listCoord, vec4(vel, 0.0));

//  imageAtomicAdd(voxelNX, cellUV, int(mp.x*65535.0));
//  imageAtomicAdd(voxelNY, cellUV, int(mp.y*65535.0));
//  imageAtomicAdd(voxelNZ, cellUV, int(mp.z*65535.0));

//  imageStore(voxelNX, cellUV, ivec4(normal.x*65535.0));
//  imageStore(voxelNY, cellUV, ivec4(normal.y*65535.0));
//  imageStore(voxelNZ, cellUV, ivec4(normal.z*65535.0));

  discard;

  return;

 // frag.rgb = diffuse.rgb*diffuse.rgb;
 // frag.a = 1.0;

}

