#version 450 core

// inputs 
in vec2 texCoord;

// outputs
layout (location = 0) out vec4 fragColor;

// uniforms
uniform float       g_time;
uniform ivec3       g_resolution;
uniform vec2        g_projectionParams;
uniform vec3        g_origin;
uniform vec3        g_target;
uniform mat4        m_proj;
uniform mat4        m_view;

// ------------------------
#define PI 3.141592653589793238
 
#define MAX_STEPS 128
#define MAX_DIST  64.
#define EPS_RAYMARCH .0001
 
// cylindrical vec3 is defined as x = r, y = phi and z = z
 
mat3 calcLookAtMatrix(vec3 origin, vec3 target, float roll) {
  vec3 rr = vec3(sin(roll), cos(roll), 0.0);
  vec3 ww = normalize(target - origin);
  vec3 uu = normalize(cross(ww, rr));
  vec3 vv = normalize(cross(uu, ww));
 
  return mat3(uu, vv, ww);
}
 
vec3 cart2Cyl(vec3 a) {
    return vec3(length(a.xy), atan(a.y, a.x), a.z); 
}
 
float sdBox( vec3 p, vec3 b )
{
  vec3 q = abs(p) - b;
  return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
}
 
 
mat2 rotate(float a) {return mat2(cos(a), -sin(a), sin(a), cos(a));}
vec3 rotateXYZ(vec3 o, vec3 a) {
  o.yz *= rotate(a.x);
  o.xz *= rotate(a.y);
  o.xy *= rotate(a.z);
 
  return o;
}
 
vec3 rotateZYX(vec3 o, vec3 a) {
  o.xy *= rotate(a.z);
  o.xz *= rotate(a.y);
  o.yz *= rotate(a.x);
 
  return o;
}
 
// domain repetition
 
float rep1(float p, float rep) {
    return float(mod(p+.5*rep, rep)-.5*rep);
}
 
vec2 rep2(vec2 p, vec2 rep) {
    return vec2(mod(p+.5*rep, rep)-.5*rep);
}
 
vec3 rep3(vec3 p, vec3 rep) {
    return vec3(mod(p+.5*rep, rep)-.5*rep);
}
 
// domain repetition - bounded
 
float repLim1(float p, float c, float l)
{
    return p-c*clamp(round(p/c),-l,l);
}
 
vec2 repLim2(vec2 p, vec2 c, vec2 l)
{
    return p-c*clamp(round(p/c),-l,l);
}
 
vec3 repLim3(vec3 p, vec3 c, vec3 l)
{
    return p-c*clamp(round(p/c),-l,l);
}
 
float mapRings(vec3 pc, vec3 size, vec3 adjust, vec3 reps) {
    pc += adjust;// pre-transform
    pc  = repLim3(pc, reps, vec3(2., 30000., 400.));
    return sdBox(pc, size);
}
 
float mapObject(vec3 p, vec3 t) {
   vec3 pp = p-t; // BIG PP ENER....ehm hold up
   for (int i = 0; i < 3; i++) {
       pp = abs(pp) - vec3(0.2);
       //pp += vec3(0.04, 0.09, 0.08);
       pp = rotateXYZ(pp, vec3(
          float(i+1)*g_time*0.2,
          float(i+1)*g_time*0.2,
          float(i+1)*g_time*0.2)
       );
   }
   return sdBox(pp, vec3(0.3));
}
 
float map(vec3 p, vec3 t) {
 
 
    float a, b = mapObject(p, t);
 
    p.xy = rep2(p.xy, vec2(40.));
    vec3 pc = cart2Cyl(p);
    vec3 pco = pc;
    pc.xz  = rep2(pc.xz, vec2(20.0, 18.0));
    pc.xz *= rotate(g_time*0.1);
    pc.xy = rep2(pc.xy, vec2(14.));
 
    a =        (mapRings(pc,  vec3(0.1, 0.3, 1.), vec3(0.5, -g_time * 0.7, 0.), vec3(16., PI/3.0,  4.)));
    a = min(a,  mapRings(pc,  vec3(0.1, 0.2, 1.), vec3(2.2, +g_time * 0.8, 0.), vec3(16., PI/2.5,  3.)));
    a = min(a,  mapRings(pc,  vec3(0.1, 0.4, 1.), vec3(3.6, -g_time * 0.9, 0.), vec3(16., PI/1.5,  8.)));
    a = max(a, -mapRings(pco, vec3(2.5, 4., 5.), vec3(0, 0., 0.), vec3(8., PI/4.0,  8.)));
 
    return min(a, b);
}
 
// x = t, y = occluison factor, z - distance to nearest occluder
vec3 raymarchTranslucent(vec3 o, vec3 target, vec3 d, float att) {
    float t = 0.;
    float occ = 0.;
    float dist = 0.;

    for (int i = 0; i < MAX_STEPS; i++) {
        vec3 p = o + d*t;
        float ct = max(abs(map(p, target)), EPS_RAYMARCH);
        float a = exp(-ct*att);
 
        // lightstreak :)
        float ls = mod(length(p) + 40.0*g_time, 60.0);
        if (ls < 10.0) {
            a *= 32.0*smoothstep(0., 10., ls);
        }
 
        occ += a;
        t += ct * 0.5;
        if ((ct == EPS_RAYMARCH) && (dist == 0)) dist = t;
        //t += ct;
    }
 
    return vec3(t, occ, dist == 0. ? t : dist);
}
 
// iq's color ramp :)
vec3 colorRamp(float t, vec3 a, vec3 b, vec3 c, vec3 d )
{
    return a + b*cos( 2.*PI*(c*t+d) );
}
 
void main()
{
    vec2 uv = (2.*(texCoord) - 1.) * vec2(float(g_resolution.x) / g_resolution.y, 1.); // uniform rendertarget coords

    // https://www.shadertoy.com/view/MtV3Dd
    float aperture = g_projectionParams.x * 2.0*PI;
    float f = 1.0/aperture;
    float r = length(uv);
    float phi = atan(uv.y, uv.x);
    float theta = atan(r/((1.+g_projectionParams.y)*f))*(1.+g_projectionParams.y);
    
    vec4 ray = vec4(sin(theta)*cos(phi), sin(theta)*sin(phi), -cos(theta), 1.0);
 
    ray *= m_view;
 
    vec3 occ = raymarchTranslucent(g_origin, g_target, ray.xyz, 14.);
    vec3 p = g_origin + occ.x*ray.xyz;           // intersection point
 
    vec3 tint = colorRamp((g_origin.z*3.+p.x*p.y)*0.0001+p.z*0.001,
        vec3(0.61, 0.54, 0.59), // a
        vec3(0.51, 0.48, 0.54), // b
        vec3(1.0, 1.0, 0.5), // c
        vec3(0.80, 0.90, 0.30)  // d
    );
    
    fragColor  = vec4(max(tint*vec3(occ.y*.012)*(1. - occ.x*0.003), 0.), 1.);

    float depth = length(p - g_origin);
    float d = (m_proj[2][2]*depth - m_proj[3][2])/(-depth);
    gl_FragDepth = d;
}






