// Code by Flopine

// Thanks to wsmind, leon, XT95, lsdlive, lamogui,
// Coyhot, Alkama,YX, NuSan and slerpy for teaching me

// Thanks LJ for giving me the spark :3

// Thanks to the Cookie Collective, which build a cozy and safe environment for me
// and other to sprout :)  https://twitter.com/CookieDemoparty

#define BPM (150./60.)
#define cucubspeed (BPM/2.)

#define PI acos(-1.)
#define TAU 6.283185
#define ITER 64.
#define time(speed) fract(iTime*speed)
#define bouncy(speed) sqrt(abs(sin(time(speed)*PI)))
#define animback(speed) easeInOutBack(abs(-1.+2.*time(speed)))
#define animcirc(speed) easeInOutCirc(time(speed))
#define switchanim(speed) floor(sin(time(speed)*2.*PI)+1.)


float easeInOutCirc(float x)
{
    return x < 0.5
        ? (1. - sqrt(1. - pow(2. * x, 2.))) / 2.
        : (sqrt(1. - pow(-2. * x + 2., 2.)) + 1.) / 2.;
}

float easeInOutBack(float x)
{
    float c1 = 1.70158;
    float c2 = c1 * 1.525;

    return x < 0.5
        ? (pow(2. * x, 2.) * ((c2 + 1.) * 2. * x - c2)) / 2.
        : (pow(2. * x - 2., 2.) * ((c2 + 1.) * (x * 2. - 2.) + c2) + 2.) / 2.;
}

float hash21(vec2 x)
{return fract(sin(dot(x,vec2(10.4,32.4))*1284.4));}

mat2 rot (float a)
{return mat2(cos(a),sin(a),-sin(a),cos(a));}

float stmin (float a, float b, float k, float n)
{
    float st = k/n;
    float u = b-k;
    return min(min(a,b), 0.5*(u+a+abs(mod(u-a+st,2.*st)-st)));
}

float smin( float a, float b, float k )
{
    float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 );
    return mix( b, a, h ) - k*h*(1.0-h);
}

float box(vec3 p, vec3 c)
{
    vec3 q = abs(p)-c;
    return min(0.,max(q.x,max(q.y,q.z)))+length(max(q,0.));
}

float od (vec3 p ,float d)
{return dot(p,normalize(sign(p)))-d;}

float cyl (vec3 p, float r, float h)
{return max(length(p.xy)-r,abs(p.z)-h);}

float g1 = 0.;
float room (vec3 p, float size)
{
    p.z += (floor(iTime*cucubspeed)+animcirc(cucubspeed))*1.6;
    vec3 pp = p;
    float d = abs(box(p, vec3(size,3.,1e10)))-0.2;
    float per = 2.;
    p.z = mod(p.z, per)-per*0.5;
    p.x = abs(p.x)-size*0.95;
    d = stmin(d, cyl(p.xzy, 0.3,3.),0.2,3.);

    p = pp;
    p.x = abs(p.x)-size*0.6;
    p.y += 1.5;
    p.z = mod(p.z-per*.5, per)-per*0.5;
    p.xz *= rot(time(0.5)*TAU);
    float gem = od(p,0.5);
    g1 += 0.01/(0.01+gem*gem);
    d = min(d,gem);

    p = pp;
    p.x = abs(p.x)-size*0.6;
    p.y += 2.5;
    d = stmin(d,box(p,vec3(0.8,0.2,1e10)),0.5,4.);

    p = pp;
    d = max(p.y-2.,d);
    return d;
}

float cucube (vec3 p, float speed)
{
    float size = 0.8;
    p.z -= 2.;
    p.x = abs(p.x)-(.9+animback(BPM/8.));
    p.y += 2.5-size;
    p.y -= bouncy(speed);
    p.yz *= rot(animcirc(speed)*PI/2.);
    return box(p,vec3(size));
}

float rom,cub;
float SDF (vec3 p)
{
    vec3 pp = p;
    p.yz *= rot(-atan(1.));
    p.xz *= rot(PI/4.);
    vec3 piso = p;

    float size = 7.;
    rom = room(p,size);
    float d = rom;

    p = piso;
    cub = cucube(p,cucubspeed);
    d = min(d,cub);
    return d;
}

vec3 getnorm (vec3 p)
{
    vec2 eps = vec2(0.01,0.);
    return normalize(SDF(p)-vec3(SDF(p-eps.xyy),SDF(p-eps.yxy),SDF(p-eps.yyx)));
}

float mask (vec2 uv)
{
    return smoothstep(0.1,
                      0.2,
                      sin(abs(length(uv+vec2(0.1,-0.1))-time(BPM/8.+PI/2.))*PI)
                     );
}

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    vec2 uv = vec2(fragCoord.x / iResolution.x, fragCoord.y / iResolution.y);
    uv -= 0.5;
    uv /= vec2(iResolution.y / iResolution.x, 1);

    float dither = hash21(uv);

    vec3 ro = vec3(uv*10.,-50.),
        rd = vec3(0.,0.,1.),
        p = ro,
        l = normalize(vec3(1.,1.,-1.)),
        col = vec3(0.,0.01,0.02);

    float shad, d = 0.; bool hit = false;

    for (float i=0.; i<ITER;i++)
    {
        d = SDF(p);
        if (d<0.001)
        {
            hit = true;
            shad = i/ITER;
            break;
        }
        rd += (1.-mask(uv))*0.005;
        d *= 0.9 + dither*0.1;
        p += d*rd;
    }

    if (hit)
    {
        if (d == rom) col = vec3(1.);
        if (d == cub) col = vec3(0.4,0.,0.3);
        vec3 n = getnorm(p);
        float lighting = dot(n,l)*.5+.5;
        col *= mix(vec3(0.3,0.0,0.1),vec3(0.7,0.5,0.8),lighting);
    }
    col += g1*vec3(0.1,0.6,0.8)*0.1;
    fragColor = vec4(sqrt(col),1.);
}
