#version 430

in vec2 uv;

layout(location = 0) out vec4 frag;

uniform float g_time;


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


// TODO #include "noise.h"


uniform float noiseOct = 4.0;
uniform float noiseOctMul = 3.0;
uniform float noiseOctAmp = 0.75;
uniform float noiseSplineEnabled = 1.0;

uniform float shape = 0.0;

// 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)-0.0;
}
float turb3(vec3 c) {
        float r=0.0;
        float s=0.5;
        for (int i=0;i<noiseOct;i++) {
                r+=s*(perlin(c));
                c*=noiseOctMul;
                s*=noiseOctAmp;
        }
        return r;
}

vec3 posd(float pw) {
    return vec3(sin(pw)*17.0, cos(pw*1.7)*13.0, sin(pw*0.78+cos(pw*0.4))*11.0)*32.0;
}

float getSplineValue(float t, float p0, float pt0, float p1, float pt1) {
    float t2 = t*t;
    float t3 = t*t*t;
    float p = (2.0*t3-3.0*t2+1.0)*p0+(t3-2.0*t2+t)*pt0+(-2.0*t3+3.0*t2)*p1+(t3-t2)*pt1;
    return p;
}

float perlin4(vec4 p) {
//    vec4 pf1 = floor(p);
//    vec4 pf2 = floor(p+vec4(1.0));

    float p1 = perlin(p.xyz+posd(floor(p.w)+1.0));
    float p2 = perlin(p.xyz+posd(floor(p.w)+2.0));

    if (noiseSplineEnabled > 0.5) {
        float p0 = perlin(p.xyz+posd(floor(p.w)));
        float p3 = perlin(p.xyz+posd(floor(p.w)+3.0));
        float ki = fract(p.w);
        return getSplineValue(ki, p1, p2-p0, p2, p3-p1);
    } else {
        float ki = smoothstep(0.0, 1.0, fract(p.w));
        return p1*(1.0-ki)+p2*(ki);
    }
}

float turb4(vec4 c) {
        float r=0.0;
        float s=0.5;
        for (int i=0;i<int(noiseOct);i++) {
                r+=s*(perlin4(c));
                c*=noiseOctMul;
                s*=noiseOctAmp;
        }
        return r;
}

// end of "noise.h"


uniform float noisePos = 0.0;
uniform float noiseOfsX = 0.0;
uniform float noiseOfsY = 0.0;
uniform float noiseFreq = 1.0;

uniform float dispAmp = 1.0;
uniform float dispSub = 0.0;
uniform float dispExp = 1.0;
uniform float dispMinLim = 0.0;
uniform float dispMaxLim = 999.0;
uniform float dispSmooth = 0.0;

layout(binding=0, rgba32i) uniform iimage2D mesherTriangles;
layout(binding=1, r32i) uniform iimage2D mesherVertPriTri;
layout(binding=2, rgba32i) uniform iimage2D mesherTriAdjLeft;
layout(binding=3, rgba32i) uniform iimage2D mesherTriAdjRight;


float deg2pi = 2.0*3.141592/360.0;

uniform float div = 1.0;

// generate a planar mesh grid with chosen refinement given through div settings

void main() {
    ivec2 uvs = ivec2(gl_FragCoord.xy);

    vec4 screen = vec4(0.0);

    ivec2 triBufSize = imageSize(mesherTriangles);

    int divi = int(div);

    if (divi <= 0) return;

    int pid = uvs.x+uvs.y*triBufSize.x;

    if (pid < (divi+1)*(divi+1)) {
        // store the "primary" vertex of the triangle,
        // purpose of this is to have some triangle for each vertex marked so can iterate all attached triangles through
        // when calculating the vertex normal
        int vid = pid;
        int vr = vid/(divi+1);
        if (vid-vr*(divi+1) > divi-1) { // last vertex of row
            imageStore(mesherVertPriTri, uvs, ivec4((vid-vr)*2-1));
        } else if (vr > divi-1) { // last row
            imageStore(mesherVertPriTri, uvs, ivec4((vid-vr)*2-(divi*2)));
        } else {
            imageStore(mesherVertPriTri, uvs, ivec4((vid-vr)*2));
        }
    }

    // 0  1  2  3  4
    // 5  6  7  8  9
    // 10 11 12 13 14

    // tri 0: 0,1,5
    // tri 1: 1,6,5
    // tri 2: 1,2,6
    // tri 3: 2,7,6
    // ..


    if (pid > divi*divi*2) { discard; return; }

    int tid = pid;
    int tido = tid/2;
    ivec3 tvids = ivec3(0);
    // triangle row, rounded down
    int tr = tido/(divi);

//   if (tid > 7) return;
 //   tvids.x = 0;
    ivec3 adjL = ivec3(-1);
    ivec3 adjR = ivec3(-1);

    if (shape < 0.5) {
      // plane
      if ((tid&1) == 0) {
          tvids.x = tr+tido+0;
          tvids.y = tr+tido+1;
          tvids.z = tr+tido+(divi+1);

          if (tido-tr*divi > 0) { // not first on the row
              adjL.x = tid-1;
              adjR.z = tid-1;
          }
          adjL.z = tid+1;
          adjR.y = tid+1;
          if (tr > 0) { // not on first row
              adjL.y = tid-(divi*2)+1;
              adjR.x = tid-(divi*2)+1;
          }
      } else {
          tvids.x = tr+tido+1;
          tvids.y = tr+tido+(divi+1)+1;
          tvids.z = tr+tido+(divi+1);

          if (tido-tr*divi < divi-1) { // not last on the row
              adjL.y = tid+1;
              adjR.x = tid+1;
          }
          adjL.x = tid-1;
          adjR.z = tid-1;
          if (tr < divi) { // not on the last row
              adjL.z = tid+(divi*2)-1;
              adjR.y = tid+(divi*2)-1;
          }
      }
    } else {
      // ball
      if ((tid&1) == 0) {

          if (tido-tr*divi < divi-1) { // not last on the row
            tvids.x = tr+tido+0;
            tvids.y = tr+tido+1;
            tvids.z = tr+tido+(divi+1);
          } else {
            tvids.x = tr+tido+0;
            tvids.y = tr+tido-(divi+1)+1; // WRAP
            tvids.z = tr+tido+(divi+1);
          }

          if (tido-tr*divi > 0) { // not first on the row
              adjL.x = tid-1;
              adjR.z = tid-1;
          } else {
             adjL.x = tid+divi*2; // WRAP
             adjR.z = tid+divi*2; // WRAP
          }

          adjL.z = tid+1;
          adjR.y = tid+1;
          if (tr > 0) { // not on first row
              adjL.y = tid-(divi*2)+1;
              adjR.x = tid-(divi*2)+1;
          }
      } else {
          if (tido-tr*divi < divi-1) { // not last on the row
            tvids.x = tr+tido+1;
            tvids.y = tr+tido+(divi+1)+1;
            tvids.z = tr+tido+(divi+1);
          } else {
            tvids.x = tr+tido-(divi+1)+1;  // WRAP
            tvids.y = tr+tido+1;  // WRAP
            tvids.z = tr+tido+(divi+1);
           }

          if (tido-tr*divi < divi-1) { // not last on the row
              adjL.y = tid+1;
              adjR.x = tid+1;
          } else {
              adjL.y = tid-divi*2; // WRAP
              adjL.x = tid-divi*2; // WRAP
          }
          adjL.x = tid-1;
          adjR.z = tid-1;
          if (tr < divi) { // not on the last row
              adjL.z = tid+(divi*2)-1;
              adjR.y = tid+(divi*2)-1;
          }
      }
    }
    imageStore(mesherTriangles, uvs, ivec4(tvids, 0));

    imageStore(mesherTriAdjLeft, uvs, ivec4(adjL, 0));
    imageStore(mesherTriAdjRight, uvs, ivec4(adjR, 0));



    discard;

    screen.a = 1.0;
    frag = screen;


}
