uniform float iGlobalTime;
uniform vec2 iResolution;
out vec4 fragColor;

#define DUR (5.323173125 / 4.0) // duration
#define REPEATS 16.0
#define TOTALDUR (DUR * REPEATS)
#define SPD 1.0 // timescale
#define OFF 0.0 // time offset for audio timings

float rand1d(float n)
{
	return fract(sin(n) * 43758.5453);
}

float time()
{
    return mod((iGlobalTime + OFF) * SPD, DUR * REPEATS) / DUR;
}

float time(float offset)
{
    return (mod((iGlobalTime + OFF) * SPD, DUR * REPEATS) + offset) / DUR;
}

float quart()
{
    return mod(time(), 0.25);
}

float quart(float offset)
{
    return mod(time(offset), 0.25);
}

float halft()
{
    return mod(time(), 0.5);
}

float halft(float offset)
{
    return mod(time(offset), 0.5);
}

float wholet()
{
    return mod(time(), 1.);
}

float wholet(float offset)
{
    return mod(time(offset), 1.);
}

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 displacement(vec3 p, float k)
{
	return sin(k*p.x)*sin(k*p.y)*sin(k*p.z);
}

float scene(vec3 p)
{
    pR(p.xy, max(0., iGlobalTime - (TOTALDUR - DUR * 8)) * 0.5);
    pR(p.xy, max(0., iGlobalTime - (TOTALDUR - DUR * 7)) * 0.5);
    pR(p.xy, max(0., iGlobalTime - (TOTALDUR - DUR * 5)) * 0.5);
    pR(p.zx, max(0., iGlobalTime - (TOTALDUR - DUR * 4)) * 1.2);
    pR(p.yx, max(0., iGlobalTime - (TOTALDUR - DUR * 3)) * 1.4);
    pR(p.xz, max(0., iGlobalTime - (TOTALDUR - DUR * 3)) * 1.4);
    // pR(p.zy, -max(0., iGlobalTime - (TOTALDUR - DUR * 3)) * 1.5);

    float i = floor(iGlobalTime / DUR);
    float j = max(0., i - 3.);
    float k = max(0., j - 4.);

    // pR(p.zx, sin(time()) * 0.4);
    // pR(p.yz, cos(time()) * 0.4);

    float spacing = 2.2 -clamp(iGlobalTime - (TOTALDUR - DUR * 1), 0., 0.2);
    // spacing += clamp(iGlobalTime - (TOTALDUR - DUR * 1.3), 0., 0.4);
    pModInterval1(p.y, spacing, -clamp(i, 0., 4.), clamp(i, 0., 4.));
    pModInterval1(p.x, spacing, -clamp(j, 0., 4.), clamp(j, 0., 4.));
    pModInterval1(p.z, spacing, 0., k);

    float box = fBox(p, vec3(1.0));

    return box;
}

void main()
{
    vec2 fragCoord = gl_FragCoord.xy;

    vec3 cameraOrigin = vec3(.0, .0, 8.0) * 0.4;
    cameraOrigin *= 1. + iGlobalTime * 0.2;

    // pR(cameraOrigin.yz, iGlobalTime);
    // pR(cameraOrigin.xy, max(0., iGlobalTime - (TOTALDUR - DUR * 8)));

    cameraOrigin *= 1. + max(0., iGlobalTime - (TOTALDUR - DUR * 4)) * 0.5;
    // cameraOrigin *= 1. + max(0., iGlobalTime - (TOTALDUR - DUR * 1)); // final zoom

    //cameraOrigin += vec3(sin(iTime) * 3., sin(iTime * 0.4) * 0.5, sin(iTime) * 1.); // gungis
    //cameraOrigin += vec3(0., 0., -iTime * 25.); 
    vec3 cameraTarget = vec3(.0, .0, .0);
    vec3 upDirection = vec3(.0, 1.0, .0);
    //upDirection *= rotZ(sin(iTime * 2.) * 0.1); // gungis
    //upDirection *= rotX(-abs(sin(iTime * 0.3)) * 0.7);
    vec3 cameraDir = normalize(cameraTarget - cameraOrigin);
    vec3 cameraRight = normalize(cross(upDirection, cameraOrigin));
	vec3 cameraUp = cross(cameraDir, cameraRight);
    
    
    vec2 screenPos = -1.0 + 2.0 * fragCoord.xy / iResolution.xy;
	screenPos.x *= iResolution.x / iResolution.y;
    screenPos.y *= 1.5;

    // fx
    float scaleN = max(.0, 0.05 - wholet());
    screenPos *= 1. - scaleN * 5.; // scale
    float shakeN = max(.0, 0.1 - wholet(-0.02));
    screenPos += (vec2(rand1d(iGlobalTime), rand1d(iGlobalTime + 1.0)) - 0.5) * 1.0 * shakeN; //shake
    
    //screenPos += (vec2(random1d(iTime),random1d(iTime + 1.0)) - 0.5)* 0.02; //shake
    
    vec3 rayDir = normalize(cameraRight * screenPos.x + cameraUp * screenPos.y + cameraDir);
    
    const float MAX_DIST = 100.0;
    const float EPSILON = 0.001;

    float totalDist = 0.0;
    vec3 pos = cameraOrigin;
    float dist = EPSILON;

    for(int i = 0; i < 200; i++)
    {
        if (dist < EPSILON || totalDist > MAX_DIST)
            break;

        dist = scene(pos);
        totalDist += dist;
        pos += dist * rayDir;
    }
    
    vec3 bgColor = vec3(0.25, 0.56, 0.96) * 0.4;
    vec3 fadeColor = vec3(1.0, 0.3, 0.05) * 1.0;
    
    if(dist < EPSILON)
    {
        vec2 eps = vec2(0.0, EPSILON);
        vec3 normal = normalize(vec3(
            scene(pos + eps.yxx) - scene(pos - eps.yxx),
            scene(pos + eps.xyx) - scene(pos - eps.xyx),
            scene(pos + eps.xxy) - scene(pos - eps.xxy)));
        
        float diffuse = max(.0, dot(-rayDir, normal));
        float specular = pow(diffuse, 60.0);
        
        vec3 color = vec3(diffuse + specular);
        color = color * normalize(fadeColor) * 0.7 + bgColor * 0.0 + fadeColor * 0.8;
        
        fragColor = vec4(color, 1.0);
    }
    else
    {
        fragColor = vec4(bgColor, 1.);
    }

    float colorFactor = max(0.01, 1.0 - (wholet(-0.01)) * 10.0);
    fragColor = round(fragColor / colorFactor) * colorFactor;
}