// "Voro Nope" 4k intro, released at Assembly 2016
// Created by Jere "XMunkki" Sanisalo
//
// Parts from the internet, Inigo Quilez and others..
//

vec2 p;
float time;
////////////////




int fx;
float fx_part;

//#define pi 3.14159265
//#define pi2 (pi * 2.0)

float smoothlerp(float v1, float v2, float t)
{
	return mix(v1, v2, t*t*(3.0-2.0*t));
}

float Randf_from_2(vec2 co)
{
    return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
}

vec3 Rand3(vec3 p)
{
	return vec3(
		Randf_from_2(p.yz),
		Randf_from_2(p.xz),
		Randf_from_2(p.xy));
}

float sdBox( vec3 p, vec3 b )
{
  vec3 d = abs(p) - b;
  return min(max(d.x,max(d.y,d.z)),0.0) +
         length(max(d,0.0));
}

int imod(int ai, int bi)
{
    return int(mod(float(ai), float(bi))+0.001);
}

bool Is_Inside(vec3 p)
{
    // Pick the effect
    int f = imod(fx, 4);

    if (fx < (16 * 2)) f = 4; // Start ball

    // 0
    if (f-- == 0)
    { // Cross and ball
		p = abs(p);
        return ((length(p) < 4.0) ||
            (max(p.x, p.y) < 1.0) ||
            (max(p.x, p.z) < 1.0) ||
            (max(p.z, p.y) < 1.0));
    }

    // 1
    if (f-- == 0)
    { // Torus
		vec2 q = vec2(length(p.xz)-5.0,p.y);
		return (length(q) < 2.0);
    }

    // 2
    if (f-- == 0)
    { // Box
        return sdBox(p, vec3(5,5,5)) < 0.0;
    }

    // 3
    if (f-- == 0)
    { // Corner boxes
        float d = sdBox(p, vec3(6,6,6));
        float sz = 2.0;
        p = abs(p);
        d = max(d, -(p.x - sz));
        d = max(d, -(p.y - sz));
        d = max(d, -(p.z - sz));

        return d < 0.0;
    }

    // 4
    if (f-- == 0)
    { // Ball
        return length(p) < 4.0;
    }

    return false;
}

vec3 CalcOffset(ivec3 ipos)
{
    vec3 pos = vec3(ipos);
    vec3 p = abs(Rand3(pos));
    vec3 ang = vec3(time * 1.23, time * 0.84, time * 1.11) + p.zxy * 234.456;
    return clamp(p + cos(ang), vec3(0.2,0.2,0.2), vec3(0.8,0.8,0.8));
}

float DF(vec3 wpos, vec3 frac_pos_tweak)
{ // Voronoi based distance function
	ivec3 pos = ivec3(floor(wpos));
	vec3 posf = fract(wpos) + frac_pos_tweak;



//	bool is_inside = Is_Inside(pos);
bool is_inside = false;

	float dist_inside = 10.0;
	float dist_outside = 10.0;
	for (int z = -1; z <= 1; ++z)
	{
		for (int y = -1; y <= 1; ++y)
		{
			for (int x = -1; x <= 1; ++x)
			{
				ivec3 iofs = ivec3(x, y, z);
				ivec3 ipos = pos + iofs;

				// Same material?
				//if (is_inside == Is_Inside(ipos))
				//	continue;

				// Apply distance
				//vec3 local_ofs = abs(Rand3(vec3(ipos)));
				//vec3 local_ofs2 = abs(Rand3(vec3(ipos+ivec3(4,4,4))));
				//vec3 local_ofs = vec3(0.5, 0.5, 0.5);

// Lerp between fixed and tweaked
//local_ofs = mix(local_ofs, vec3(0.5, 0.5, 0.5), 1.0 - abs(cos(time * 2.0)));
//local_ofs = mix(local_ofs, local_ofs2, (1.0 + cos(time * 3.0))*0.5);

                vec3 local_ofs = CalcOffset(ipos);

				vec3 fofs = vec3(iofs) - posf + local_ofs;
				float d = dot(fofs, fofs);

				//res = min(res, d);
				if (Is_Inside(vec3(ipos)))
					dist_inside = min(dist_inside, d);
				else
					dist_outside = min(dist_outside, d);
			}
		}
	}

	dist_inside = sqrt(dist_inside);
	dist_outside = sqrt(dist_outside);

	float d = dist_inside - dist_outside;
	return d;
}

vec3 DFNormal(vec3 p)
{
	float d = DF(p, vec3(0,0,0));
	float eps = 0.001;
	return normalize(vec3(
		DF(p, vec3(eps, 0, 0)) - d,
		DF(p, vec3(0, eps, 0)) - d,
		DF(p, vec3(0, 0, eps)) - d
		));
}

vec2 Raytrace(vec3 ray_pos, vec3 ray_dir)
{
	vec2 dist_glow = vec2(0, 0);

    for (int i = 0; i < 40; ++i)
	{
		float d = DF(ray_pos + ray_dir * dist_glow.x, vec3(0,0,0));
		if (abs(d) < 0.001)
			break;

        // Glow
        if (d > 0.0)
            dist_glow.y += max(1.0 - d * 1.0, 0.0) * 0.2;

        // Moving
		dist_glow.x += d * 0.4;
	}

	return dist_glow;
}

float AO(vec3 pos, vec3 normal)
{
    float t = 0.0;
    float ret = 0.0;
    for (int i = 0; i < 10; ++i)
    {
        t += 0.1;
        float d = DF(pos + normal * t, vec3(0,0,0));
        ret += clamp((t - d) * 0.2, 0.0, 1.0);
    }
    //ret *= 0.0;
    return 1.0 - ret;
}

vec3 MakeColor(int idx, float adj)
{
    float i = adj + float(idx)*1523.0;
    vec3 c = abs(cos(vec3(i,i*0.374,i*0.584)));
    if (length(c) < 0.7)
        return vec3(1,1,1)-c;
    return c;
}

float Specular(vec3 ray_dir, vec3 normal, vec3 light_dir)
{
    vec3 hv = normalize(normalize(light_dir) - ray_dir);
    return pow(abs(dot(normal, hv)), 15.0) * 2.0;
}

vec4 intro()
{
    // Pick the effect
    fx_part = time / 0.477;
    fx = int(fx_part);
    fx_part = fract(fx_part);

    // Base drum pause
    if (fx >= 61 && fx < 64)
    {
        fx = 60;
        fx_part = 1.0;
    }

    // Calculate the camera
	float cam_ang = time * 0.25;
	vec3 cam_pos;
	vec3 cam_target = vec3(0, 0, 0);
	float cam_dist = 10.0;

    cam_dist += fract(float(fx)*1234.56789) * 5.0; // Random direction

    if (fx < 32)
    {
        cam_dist = 18.0 + time * 4.5 - float(fx) * 2.5;
    }
    else
    {
        cam_ang += float(fx) * 11.0;
    }
    if (fx >= (16 * 8))
    {
        float amt = fx_part * 3.0;
        cam_dist += (imod(fx, 2) == 0) ? amt : -amt;
    }

	cam_pos = normalize(vec3(sin(cam_ang), sin(1.0 + cam_ang * 1.0) * 1.0, cos(cam_ang))) * cam_dist;

    if (fx >= (16*2))
    {
        if (imod(fx,2)==0)
            cam_pos.x *= -1.0;
        if (imod(fx,3)==0)
            cam_pos.y *= -1.0;
        if (imod(fx,5)==0)
            cam_pos.z *= -1.0;
    }

    // Create the camera vectors
	vec3 cam_z = normalize(cam_target - cam_pos);
	vec3 cam_x = normalize(cross(vec3(0,1,0), cam_z));
	vec3 cam_y = normalize(cross(cam_x, cam_z));
	vec3 ray_dir = normalize(
		cam_x * p.x/* * (16.0/9.0)*/ +
		cam_y * p.y +
		cam_z); // * 1.0    <-- fov

	// Calculate the pixel color
	vec3 color;
	vec2 dist_glow = Raytrace(cam_pos, ray_dir);

	if (dist_glow.x < 20.0)
	{
		vec3 hit_pos = cam_pos + ray_dir * dist_glow.x;
		vec3 normal = DFNormal(hit_pos);

		float light = abs(dot(normal, normalize(vec3(1,1,1))));

        // Specular lighting
        light += Specular(ray_dir, normal, vec3(-1,1,1));
        light += Specular(ray_dir, normal, vec3(1,-1,1));
        light += Specular(ray_dir, normal, vec3(1,1,-1));

        int col_idx = (fx < 32) ? -1 : fx;

        //color = Rand3(floor(hit_pos*0.21)) * 2.0;
        color = MakeColor(col_idx+2,length(hit_pos)*0.1);

        color *= light;

        // Pulse
        //c *= 2.0 - pow(0.2+abs(fx_part * 10.0 - length(hit_pos)), 1.0);

        color *= AO(hit_pos, normal);

        // Disable glow for hits
        dist_glow.y = 0.0;

        // Blackness at the end
        if (fx >= (16*6) && fx < (16*7))
            color *= 0.0;
	}
	else
	{
		//color = vec4(0,0,0.5,1);

        // Bg rotation
        float a = time * 0.5;
        vec2 asc = vec2(sin(a), cos(a));
        bool is_rot = (fx < (16 * 9));
        vec2 pr = vec2(dot(p,asc.yx), dot(p,asc * vec2(1.0,is_rot?-1.0:1.0)));

        // Circles
        float c = length(pr) * 20.0;

        int bg = imod(fx, 4);
        // L-shapes
        if (--bg == 0)
	        c = min(abs(pr.x), abs(pr.y)) * -30.0;

        // X-shapes
        if (--bg == 0)
	        c = min(abs(dot(pr, vec2(1,1))), abs(dot(pr, vec2(-1,1)))) * 20.0;

        // Boxes
        if (--bg == 0)
            c = max(abs(pr.x), abs(pr.y)) * 30.0;

        // THRESHOLD: black / area
        c = max(0.00001, cos(c - time*5.0));
        c = pow(c,0.1);


        //color = vec4(c*0.86,c*0.92,c*0.82,1);
        color = c*MakeColor(fx,length(pr));

        // Start: Bg pulse
        if (fx < (16 * 6))
        {
	        c = pow(length(pr)/1000.0,0.1)*0.4;
            color=vec3(c, c, c);
        }
    }

    // Glow
    if ((fx >= (16*6) && fx < (16 * 7)) ||
        (fx >= (16*10)))
    {
	    //color += vec3(0.98, 0.89, 0.92) * dist_glow.y;
	    color += MakeColor(fx+2,0.0) * dist_glow.y;
    }

    // Color grading
    float gr = 0.485;
    //color = pow(color, vec4(gr,gr,gr,1));

    // Vignette
    color *= 1.0 - pow(length(p) * 0.4, 0.7);

    // Flashing
    if (fx < (16 * 10))
    {
	    //color *= pow(min(fx_part * 0.5, 1.0), 0.5);
    	//color *= min(fx_part * 1.0, 1.0);
   	 	color *= smoothstep(0.0, 0.7, fx_part);
    }

    // End fade
    color *= 1.0 - smoothstep(85.0, 97.0, time);

	return vec4(color, 1.0);
}





/////////////////////////////////

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 uv = fragCoord.xy / iResolution.xy;
    uv = uv*2.0-1.0;

    p = uv;
    time = iTime;
    fragColor = intro();
}
