#define STEPS 220.0
#define MDIST 150.0
#define pi 3.1415926535
#define rot(a) mat2(cos(a),sin(a),-sin(a),cos(a))
#define pmod(p,x) (mod(p,x)-0.5*(x))
vec3 glow = vec3(0);
vec3 rlim(vec3 p, float c, vec3 l ){
    return p-c*clamp(round(p/c),-l,l);
}
float h21(vec2 a){
    return fract(sin(dot(a,vec2(43.234,25.433)))*944.3434);
}
float frame(vec3 p, vec3 b, float e){
  p = abs(p)-b;
  vec3 q = abs(p+e)-e;
  return min(min(
      length(max(vec3(p.x,q.y,q.z),0.0))+min(max(p.x,max(q.y,q.z)),0.0),
      length(max(vec3(q.x,p.y,q.z),0.0))+min(max(q.x,max(p.y,q.z)),0.0)),
      length(max(vec3(q.x,q.y,p.z),0.0))+min(max(q.x,max(q.y,p.z)),0.0));
}
/*
float ebox(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);
}
*/
float box(vec3 p, vec3 b){
    vec3 d= abs(p)-b;
    return max(d.x,max(d.y,d.z));
}
//https://www.desmos.com/calculator/dxixbmrvmb
float easeOutElastic(float x) {
    float c4 = (2. * pi) / 3.;
    return x = pow(1.63,-10.*x) * sin((x*5.25-.75)*c4)+1.;
}
//I would like to make a more procdeural version of this but so far this one still
//looks the best. 
float easeOutBounce(float x) {
    float n1 = 7.5625;
    float d1 = 2.75;
    if (x < 1. / d1) {
        return n1 * x * x;
    } else if (x < 2. / d1) {
        return n1 * (x -= 1.5 / d1) * x + 0.75;
    } else if (x < 2.5 / d1) {
        return n1 * (x -= 2.25 / d1) * x + 0.9375;
    } else {
        return n1 * (x -= 2.625 / d1) * x + 0.984375;
    }
}
//https://www.shadertoy.com/view/fdsXRf
float timeRemap (float t, float s1, float s2){
    return (s1-s2)*(fract(t)*step(1.0,mod(t,2.0))+floor(t*0.5))+t*s2;
}
vec2 map(vec3 p){
    float t = iTime*0.8*(6./5.);
    float ttt = t;
    vec3 po = p;
    vec2 a = vec2(1);
    vec2 b = vec2(2);
    float m= 2.4;
    vec2 id = floor(clamp(p.xz,-13.,13.)/m-0.5);
    float off = 0.;
    
    float cycle = mod(floor(t/4.0+0.5),6.0);
    //cycle = .0;
    if(cycle == 0.0){
        t+=length(id+1.0)*0.07;
    }
    if(cycle == 1.0){
        t+=length(id-21.0)*0.07-1.685;
    }
    if(cycle == 2.0){
        if(mod(id.y+mod(id.x,2.0),2.0)<1.0)t+=0.15;
        id = abs(id+1.);
        t+=max(id.x,id.y)*0.2;
        t-=0.15;
    }
    if(cycle == 3.0){
        t+=0.562;
        t+=id.y*0.08;
        t+=id.x*0.01;
    }
    /*
    if(cycle == 4.0){
        t+=0.4;
        //id=id+1.0;
        id.y+=1.0;
        id.x+=7.0;
        float ang = atan(id.x,id.y)*0.2;
        t+=ang;
    }
    */
    if(cycle == 4.0){
        t+=0.3;
        id+=1.0;
        t+=(mod(id.y,2.0)*3.0-2.0)*abs(id.x)*0.0175;
    }
    if(cycle == 5.0){
        t+=h21(id);
    }
    t = timeRemap(t,1.0,0.0);//changing the third value here makes some interesting bugs
    

    
    
    float tt = t;
    t = (mod(t,2.0)>1.0)?easeOutBounce(fract(t))+floor(t):easeOutElastic(fract(t))+floor(t);
    
    if(mod(tt,2.0)<1.0) off = t-floor(tt);
    else off = 1.0-(t-floor(tt));
    
    //CUBES
    p.y-=off*13.;
    p = rlim(p,m,vec3(5,0,5));
    a.x = box(p,vec3(0.99));
    //CUBE FRAMES
    b.x = frame(p,vec3(1.0),0.06)*0.8;
    float gl = 0.05;
    glow+=gl/(gl+b.x*b.x)*vec3(0.373,0.200,1.000)*clamp((0.5-abs(0.5-off))*5.0,0.0,1.0);
    
    a=(a.x<b.x)?a:b;

    //Domain fuckery
    //#define rdt(a) pow(smoothstep(0.5,1.0,abs(a)),2.0)*1.0
    vec3 d = abs(p)-vec3(m/1.7,0,m/1.7);
    vec3 d2 = abs(po)-m*5.4;
    float domBox = max(d.x,d.z);
    float outBox = max(d2.x,d2.z);
    domBox = min(-outBox,domBox);
    a.x = min(-domBox,a.x);
    
    //Floor
    p = po;
    b.x = box(p-vec3(0,-31,0),vec3(13,30,13));
    b.y = 3.0;
    a=(a.x<b.x)?a:b;
    
    //BOX RINGS
    t = ttt;
    float sz = 16.;
    p.y+=-t*14.0;
    p.y = pmod(p.y,8.0);
    b.x = box(p,vec3(sz,1,sz));
    b.x = max(-box(p,vec3(sz-1.,6,sz-1.)),b.x);
    b.x = max(po.y-3.,b.x);
    
    glow+=0.1/(0.1+b.x*b.x)*vec3(0.733,0.000,1.000)*0.2*smoothstep(4.0,-6.0,po.y);
    b.y = 5.0;
    a=(a.x<b.x)?a:b;
    
    return a;
}
vec3 norm(vec3 p){
    vec2 e= vec2(0.001,0);
    return normalize(map(p).x-vec3(
    map(p-e.xyy).x,
    map(p-e.yxy).x,
    map(p-e.yyx).x));
}

void render( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = (fragCoord-0.5*iResolution.xy)/iResolution.y;
    vec3 col = vec3(0);
    vec3 ro = vec3(0,12,-30);
    ro.zx*=rot(iTime*0.15*pi/3.*(6./5.));
    
    vec3 lk = vec3(0,2,0);
    vec3 f = normalize(lk-ro);
    vec3 r = normalize(cross(vec3(0,1,0),f));
    vec3 rd = f*0.7+uv.x*r+uv.y*cross(f,r);
    vec3 p = vec3(0);
    vec2 d = vec2(0);
    float dO = 0.;
    bool hit = false;
    for(float i = 0.; i<STEPS; i++){
        p = ro+rd*dO;
        d = map(p);
        
        if((d.x)<0.005){
            if(d.y == 5.0){
                d.x = 0.08;
            }
            else{
            hit = true;
            break;
            }
        }
        if(dO>MDIST){
            p = ro+rd*MDIST;
            dO = MDIST;
            break;
        }
        dO+=d.x*0.9;
    }
    vec3 al = vec3(0);
    if(hit){
        vec3 n = norm(p);
        vec3 r = reflect(rd, n);
        if(d.y==1.0) al = vec3(0.000,0.984,1.000)*1.4;
        if(d.y==2.0) al = vec3(0.004,0.137,0.537);
        if(d.y==3.0) {
        al = vec3(0.588,0.180,1.000);
        al+=smoothstep(0.95,1.0,sin(p.x*pi*0.75+pi/2.0))*1.6;
        al+=smoothstep(0.95,1.0,sin(p.y*pi*0.75+2.0))*1.6;
        al+=smoothstep(0.95,1.0,sin(p.z*pi*0.75+pi/2.0))*1.6;
        }
        //Lighting EQs from blackle
        float diff = length(sin(n*3.)*.5+.5)/sqrt(3.);
        float spec = length(sin(r*2.)*.4+.6)/sqrt(3.);
        col = al*diff+pow(spec,8.0)*0.4*vec3(1.000,0.722,0.910);
        #define AO(a,n,p) smoothstep(-a,a,map(p+n*a).x)
        float ao = AO(0.05,n,p)*AO(0.1,n,p);
        ao*=smoothstep(-.4,.4,map(p+n*.4).x*2.1);
        col*=pow(ao,0.7);
    }
    else{
        col = mix(vec3(0),vec3(0.055,0.000,0.161),length(pow(uv,vec2(3.0))));
    
    }
    //col = sqrt(col);
    col+=glow*0.07;
    fragColor = vec4(col,1.0);
}

#define AA 1.0
#define ZERO min(0.0,iTime)
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    float px = 1.0/AA;
    vec4 col = vec4(0);
    
    if(AA==1.0) {render(col,fragCoord); fragColor = col; return;}
    
    for(float i = ZERO; i <AA; i++){
        for(float j = ZERO; j <AA; j++){
            vec4 col2;
            vec2 coord = vec2(fragCoord.x+px*i,fragCoord.y+px*j);
            render(col2,coord);
            col.rgb+=col2.rgb;
            glow = vec3(0);
        }
    }
    col/=AA*AA;
    fragColor = vec4(col);
}
