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

//#define DUR (((16. / 181.845) * 60.) / 4.0) // duration
#define DUR (5.323173125 / 4.0) // duration
#define REPEATS 4.0
#define TOTALDUR (DUR * REPEATS)
#define SPD 0.25 // timescale
#define OFF 0.38 // 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 scene(vec3 p)
{
    float t = max(0., halft() - 0.32) * 0.8;
    float n = t * 130.0;
    float m = t * 100.0;
    float o = t * 3.0;

    // n = 0.;
    // m = 0.;
    // o = 0.;
    
    pR(p.xy, iGlobalTime * 0.7);
    pR(p.yz, iGlobalTime * 2.3);
    pR(p.zx, iGlobalTime * 1.0); 

    float sphereSize = 0.9 + floor((time(0.57)) * 2.0) * 0.3;

    float sphere = fSphere(floor(p), sphereSize + n * sphereSize * 0.7);
    float box = fBox(mod(p, (1.0 + m)), vec3(0.8 - o));

    float result = max(sphere, box);
    
    float endFlash = max(0., (iGlobalTime - (TOTALDUR * 2. - 1.)));
    result += endFlash * 2.;
    
    return min(0.1, result);
}

void main()
{
    vec2 fragCoord = gl_FragCoord.xy;
    // fragCoord.x = round(fragCoord.x / 4.) * 4.;
    // fragCoord.y = round(fragCoord.y / 6.) * 6.;

    float n = iGlobalTime;
    vec3 shake = vec3(rand1d(n), rand1d(n + 1.), rand1d(n + 2.)) * 0.04 * time();
    float distScale = 0.3 + time() * 0.1;
    vec3 cameraOrigin = vec3(1.0, -7.0, 10.0) * distScale + shake;
    vec3 cameraTarget = vec3(.0, .0, .0) + shake;
    vec3 upDirection = vec3(.0, 1.0, .0);
    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 - quart(-0.05 - 0.05));
    screenPos *= 0.9 - scaleN * 10.; // scale
    float shakeN = max(.0, 0.1 - quart(-0.1));
    screenPos += (vec2(rand1d(n), rand1d(n + 1.0)) - 0.5) * 1.0 * shakeN; //shake
    

    vec3 rayDir = normalize(cameraRight * screenPos.x + cameraUp * screenPos.y + cameraDir);
    
    const float MAX_DIST = 60.0;
    const float EPSILON = 0.0001;

    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, 20.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 - (halft(-0.03)) * 10.0);
    fragColor = round(fragColor / colorFactor) * colorFactor;
    
    float endFlash = max(0., (iGlobalTime - (TOTALDUR * 2. - 1.)));
    fragColor += endFlash * 1.;
}