/* framework header */
#version 430
layout(location = 0) out vec4 fragColor;
layout(location = 0) uniform vec4 iResolution;
layout(location = 1) uniform int iFrame;

#define PI 3.14159
#define MAX_STEPS 25
#define MIN_HIT_DIST 0.001
#define HIT_TYPE_NONE 0
#define HIT_TYPE_F 1
#define HIT_TYPE_U 2
#define HIT_TYPE_N 3
#define HIT_TYPE_C 4
#define HIT_TYPE_I 5
#define HIT_TYPE_T 6
#define HIT_TYPE_Y 7
#define HIT_TYPE_MOTEL 8
#define HIT_TYPE_MOTEL_BACKING 9
#define HIT_TYPE_CIRCLE_BAR 10
#define HIT_TYPE_CIRCLE_SPHERE 11
#define HIT_TYPE_CIRCLE_BACK_INNER 12
#define HIT_TYPE_CIRCLE_BACK_OUTER 13
#define HIT_TYPE_ARROW 14
#define HIT_TYPE_ARROW_SPHERE 15
#define HIT_TYPE_MAIN_BACKING 16
#define HIT_TYPE_POLE 17
#define HIT_TYPE_POLE_BASE 18
#define rotate(r) mat2(cos(r), sin(r), -sin(r), cos(r))
#define dot2(v) dot(v,v)

float hash2(vec2 p) {
    return fract(1532.182947938 * sin(dot(p.xy, vec2(1123.832974298, 891.92384729))));
}

float hash3(vec3 p) {
    return fract(1532.182947938 * sin(dot(p.xyz, vec3(1123.832974298, 891.92384729, 927.12897238))));
}

//iq
float sdCapsule( vec3 p, vec3 a, vec3 b, float r, float wonky_factor)
{
  vec3 pa = p - a, ba = b - a;
  float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
  h = pow(h,1. + wonky_factor);
  return length( pa - ba*h ) - r;
}

//iq
float sdLink( vec3 p, float le, float r1, float r2 )
{
  vec3 q = vec3( p.x, max(abs(p.y)-le,0.0), p.z );
  return length(vec2(length(q.xy)-r1,q.z)) - r2;
}

//iq
float sdCappedTorus( vec3 p, vec2 sc, float ra, float rb)
{
  p.x = abs(p.x);
  float k = (sc.y*p.x>sc.x*p.y) ? dot(p.xy,sc) : length(p.xy);
  return sqrt( dot(p,p) + ra*ra - 2.0*ra*k ) - rb;
}

//iq
float sdRoundBox( vec3 p, vec3 b, float r )
{
  vec3 q = abs(p) - b + r;
  return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0) - r;
}

//iq
float sdTriPrism( vec3 p, vec2 h, float r )
{
    const float k = sqrt(3.0);
    h.x *= 0.5*k;
    p.xy /= h.x;
    p.x = abs(p.x) - 1.0;
    p.y = p.y + 1.0/k;
    if( p.x+k*p.y>0.0 ) p.xy=vec2(p.x-k*p.y,-k*p.x-p.y)/2.0;
    p.x -= clamp( p.x, -2.0, 0.0 );
    float d1 = length(p.xy)*sign(-p.y)*h.x;
    float d2 = abs(p.z)-h.y;
    return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.) - r;
}

//iq
float sdRoundedCylinder( vec3 p, float ra, float rb, float h )
{
  vec2 d = vec2( length(p.xz)-ra+rb, abs(p.y) - h + rb );
  return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rb;
}

float sdTriPrism( vec3 p, vec2 h) {
     return sdTriPrism(p, h, 0.);
}

float sd_sphere(vec3 p, vec3 c, float r) {
    return length(p - c) - r;
}

struct Hit {
    int type;
    float dist;
    bool emissive;
};

struct Emitter_Hits {
    int type1;
    float dist1;
    int type2;
    float dist2;
    int type3;
    float dist3;
};

Hit map_F(vec3 p) {
    Hit hit;
    hit.emissive = true;
    hit.type = HIT_TYPE_F;
    p.xy *= rotate(-0.015);
    float r = 0.11;
    vec3 a = vec3(-1.79, 2.38, 0.);
    vec3 b = vec3(-1.42, 2.38, 0.);
    hit.dist = sdCapsule(p, a, b, r, .4);
    a = vec3(-1.79, 2.06, 0.);
    b = vec3(-1.52, 2.06, 0.);
    hit.dist = min(hit.dist, sdCapsule(p, a, b, r, .5));
    a = vec3(-1.79, 2.38, 0.);
    b = vec3(-1.79, 1.8, 0.);
    hit.dist = min(hit.dist, sdCapsule(p, a, b, r, .2));
    return hit;
}

Hit map_U(vec3 p) {
    Hit hit;
    hit.emissive = true;
    hit.type = HIT_TYPE_U;
    float r = 0.11;
    p.xy *= rotate(-0.35);
    if (p.y > 1.6) {
        hit.dist = min(sdCapsule(p, vec3(-1.89, 1.16, 0.), vec3(-1.875, 1.74, 0.), r, 0.), sdCapsule(p, vec3(-1.572, 1.16, 0.), vec3(-1.557, 1.74, 0.), r, 0.0));
        return hit;
    }
    p.xy += vec2(1.72, -1.44);
    hit.dist = sdLink(p, .16, 0.16, r);
    return hit;
}

Hit map_N(vec3 p) {
    Hit hit;
    hit.emissive = true;
    hit.type = HIT_TYPE_N;
    p.xy *= rotate(-0.21);
    p.xy += vec2(1.01, -.93);
    float r = 0.115;
    hit.dist = sdCapsule(p, vec3(0., 0.34, 0.), vec3(0., 0.93, 0.), r, 0.);
    hit.dist = min(hit.dist, sdCapsule(p, vec3(0.38, 0.35, 0.), vec3(0.365, 0.67, 0.), r, 0.0));
    p.xy -= vec2(0.18, 0.58);
    if (p.y > 0.1) {
        hit.dist = min(hit.dist, sdLink(p, .16, 0.18, r));
    }
    return hit;
}

Hit map_C(vec3 p) {
    Hit hit;
    hit.emissive = true;
    hit.type = HIT_TYPE_C;
    p.xy *= rotate(1.8);
    p.xy += vec2(-1.66, 1.1);
    float r = 0.115;
    hit.dist = sdCappedTorus(p, vec2(1.0,-0.), 0.3, r);
    return hit;
}

Hit map_I(vec3 p) {
    Hit hit;
    hit.emissive = true;
    hit.type = HIT_TYPE_I;
    p.xy *= rotate(0.179);
    p.xy += vec2(-0.92, -1.162);
    float r = 0.115;
    hit.dist = sdCapsule(p, vec3(0.38, 0.3, 0.), vec3(0.365, 0.87, 0.), r, 0.22);
    return hit;
}

Hit map_T(vec3 p) {
    Hit hit;
    hit.emissive = true;
    hit.type = HIT_TYPE_T;
    p.xy *= rotate(0.179);
    p.xy += vec2(-0.92, -1.162);
    float r = 0.115;
    hit.dist = sdCapsule(p, vec3(0.83, 0.33, 0.), vec3(0.815, 0.9, 0.), r, 0.2);
    p.xy *= rotate(PI * 0.5);
    hit.dist = min(hit.dist, sdCapsule(p, vec3(0.9, -0.63, 0.), vec3(0.915, -1., 0.), r, 0.2));
    return hit;
}

Hit map_Y(vec3 p) {
    Hit hit;
    hit.emissive = true;
    hit.type = HIT_TYPE_Y;
    p.xy *= rotate(PI);
    p.xy += vec2(1.94, 1.96);
    float r = 0.105;
    hit.dist = sdCappedTorus(p, vec2(1.0,-0.0), 0.175, r);
    hit.dist = min(hit.dist, sdCapsule(p, vec3(-0.18, -0.0, 0.), vec3(-0.2, -0.47, 0.), r, 0.2));
    p.xy += vec2(1.75, -1.0);
    p.xy *= rotate(3.14);

    Hit U;
    if (p.y > 1.4) {
        U.dist = sdCapsule(p, vec3(-1.92, 1.16, 0.), vec3(-1.92, 1.48, 0.), r, 0.2);
    } else {
        p.xy += vec2(1.74, -1.52);
        U.dist = sdLink(p, .18, 0.18, r);
    }

    hit.dist = min(hit.dist, U.dist);
    return hit;
}

Hit map_MOTEL(vec3 p) {
    Hit hit;
    hit.emissive = true;
    hit.type = HIT_TYPE_MOTEL;
    vec3 p_rot_half_pi = p;
    p_rot_half_pi.xy *= rotate(PI * 0.5);
    vec3 p_rot_pi = p;
    p_rot_pi.xy *= rotate(PI);
    // M
    hit.dist = sdRoundBox(p + vec3(0.28, -0.62, 0.), vec3(0.047,0.24,0.1), .01);
    hit.dist = min(hit.dist, sdRoundBox(p + vec3(0.555, -0.62, 0.), vec3(0.047,0.24,0.1), .01));
    vec3 large_tri_p = p_rot_pi;
    large_tri_p.x *= 2.32;
    large_tri_p.y *= 1.51;
    float large_tri = sdTriPrism(large_tri_p + vec3(-0.97, 1.055, 0.), vec2(0.5,0.1));
    float small_tri = sdTriPrism(large_tri_p + vec3(-0.97, 1.455, 0.), vec2(0.5,0.5));
    float tri = max(-small_tri, large_tri);
    hit.dist = min(hit.dist, tri);
    // O
    float O_out = sdRoundBox(p + vec3(0.43, -0.072, 0.), vec3(0.16,0.233,0.12), .07);
    float O_in  = sdRoundBox(p + vec3(0.43, -0.072, 0.), vec3(0.08,0.17,0.5), .05);
    float O = max(-O_in, O_out);
    hit.dist = min(hit.dist, O);
    // T
    hit.dist = min(hit.dist, sdRoundBox(p + vec3(0.42, 0.515, 0.), vec3(0.047,0.24,0.1), .01));
    hit.dist = min(hit.dist, sdRoundBox(p_rot_half_pi + vec3(0.29, -0.415, 0.), vec3(0.042,0.18,0.1), .01));
    // E
    hit.dist = min(hit.dist, sdRoundBox(p + vec3(0.52, 1.076, 0.), vec3(0.047,0.24,0.1), .01));
    hit.dist = min(hit.dist, sdRoundBox(p_rot_half_pi + vec3(0.87, -0.41, 0.), vec3(0.0395,0.158,0.1), .01));
    hit.dist = min(hit.dist, sdRoundBox(p_rot_half_pi + vec3(1.07, -0.41, 0.), vec3(0.0395,0.12,0.1), .01));
    hit.dist = min(hit.dist, sdRoundBox(p_rot_half_pi + vec3(1.275, -0.41, 0.), vec3(0.0395,0.152,0.1), .01));
    // L
    hit.dist = min(hit.dist, sdRoundBox(p + vec3(0.516, 1.648, 0.), vec3(0.045,0.25,0.1), .01));
    hit.dist = min(hit.dist, sdRoundBox(p_rot_half_pi + vec3(1.875, -0.41, 0.), vec3(0.0395,0.146,0.1), .01));
    // backing
    p.z += 0.08;
    float backing_dist = sdRoundBox(p + vec3(0.423,0.525,-0.1), vec3(0.23, 1.435, 0.08), 0.01);
    if (backing_dist < hit.dist) {
        hit.type = HIT_TYPE_MOTEL_BACKING;
        hit.dist = backing_dist;
        hit.emissive = false;
    }
    return hit;
}

Hit map_circle(vec3 p) {
    Hit hit;
    hit.dist = 99999999.0;
    hit.emissive = false;

    vec3 q = p;
    q.yz *= rotate(-PI * 0.5);
    float back_outer = sdRoundedCylinder(q + vec3(0.,.1,-3.28), .572, 0.015, 0.02);
    if (back_outer < hit.dist) {
        hit.dist = back_outer;
        hit.type = HIT_TYPE_CIRCLE_BACK_OUTER;
    }
    float back_inner = sdRoundedCylinder(q + vec3(0.,0.095,-3.245), .45, 0.015, 0.08);
    if (back_inner < hit.dist) {
        hit.dist = back_inner;
        hit.type = HIT_TYPE_CIRCLE_BACK_INNER;
    }
    return hit;
}

Hit map_circle_emitters(vec3 p) {
    Hit hit;
    hit.emissive = true;
    float min_sphere = 9999999.0;
    hit.dist = min_sphere;
    int count = 15;
    vec3 pos = vec3(0.52,0.,0.);
    for (int i = 0; i < count; i++) {
        float angle = float(i) / float(count) * 2.0 * PI;
        vec3 cur_pos = pos;
        cur_pos.xy *= rotate(angle);
        min_sphere = min(min_sphere, sd_sphere(p + vec3(0.,-3.25,0.), cur_pos, 0.023));
    }
    float min_bar = 9999999.0;
    for (int i = 0; i < count; i++) {
        float angle = float(i) / float(count) * 2.0 * PI;
        vec3 cur_pos = pos;
        cur_pos.xy *= rotate(angle);
        min_bar = min(min_bar, sdCapsule(p + vec3(0.,-3.25,0.), vec3(0.,0.,0.), cur_pos, 0.001, 0.));
    }
    if (min_sphere <= min_bar) {
        hit.dist = min_sphere;
        hit.type = HIT_TYPE_CIRCLE_SPHERE;
    } else {
        hit.dist = min_bar;
        hit.type = HIT_TYPE_CIRCLE_BAR;
    }
    return hit;
}

Hit map_arrow(vec3 p) {
    Hit hit;
    hit.emissive = false;
    hit.type = HIT_TYPE_ARROW;

    vec3 q = p;
    q.xy *= rotate(PI / 2.7);
    vec3 q1 = q;
    q1.y *= 2.6;
    q1.x += sin(q1.y * 2.) * 0.1;
    float tri_dist = sdTriPrism(q1 + vec3(1.85,1., -0.06) + vec3(-0.97, 1.055, 0.), vec2(0.5,0.0), 0.1) / 2.6;
    hit.dist = tri_dist;

    q1 = p;
    float wave_func = sin(p.y * 10.) * 0.05;
    q1.x += wave_func;
    hit.dist = min(hit.dist, sdRoundBox(q1 + vec3(-0.3,0.12, 0.), vec3(0.08, 1., 0.05), 0.01));

    return hit;
}

Hit map_arrow_lights(vec3 p) {
    Hit hit;
    hit.emissive = true;
    hit.type = HIT_TYPE_ARROW_SPHERE;

    vec3 q = p;
    q.xy *= rotate(PI / 2.7);
    vec3 q1 = q;
    q1.y *= 2.6;
    q1.x += sin(q1.y * 2.) * 0.1;
    float tri_dist = sdTriPrism(q1 + vec3(1.85,1., -0.06) + vec3(-0.97, 1.055, 0.), vec2(0.5,0.0), 0.1) / 2.6;
    hit.dist = 999999.0;

    q1 = p;
    float wave_func = sin(p.y * 10.) * 0.05;
    q1.x += wave_func;

    for (int x = 0; x < 3; x++) {
        for (int y = 1; y < 35; y++) {
            vec3 c = vec3(float(x), float(y), -0.1);
            c.xy *= 0.05;
            c.y -= 0.85;
            c.x += 0.245;
            c.x -= wave_func;
            float d = sd_sphere(p, c, 0.01);
            if (d < hit.dist) {
                hit.type = HIT_TYPE_ARROW_SPHERE;
                hit.emissive = true;
                hit.dist = d;
            }
        }
    }    
    for (int x = 0; x < 22; x++) {
        for (int y = 0; y < 10; y++) {
            if (y < 3) continue;
            if (x + y > 23) continue;
            if (y*2 - x > 7) continue;
            vec3 c = vec3(float(x), float(y), -0.1);
            c.z += 0.04;
            c.xy *= 0.05;
            c.xy += vec2(-1.5, -1.0);
            float d = sd_sphere(q, c, 0.01);;
            if (d <= hit.dist) {
                hit.type = HIT_TYPE_ARROW_SPHERE;
                hit.emissive = true;
                hit.dist = d;
            }
        }
    }

    return hit;
}

Hit map_main_backing_lights(vec3 p) {
    Hit hit;
    hit.emissive = true;
    hit.type = HIT_TYPE_MOTEL;
    float r = 0.006;
    
    vec3 q1 = p;
    q1.xy *= rotate(0.015);
    q1.x += 0.71;
    q1.y += sin(q1.x * 2.0) * 0.165;
    hit.dist = sdCapsule(q1 + vec3(0.0, -2.5, 0.), vec3(-0.92, 0.0, 0.), vec3(2.75, 0., 0.), r, 0.);
    
    vec3 q2 = p;
    q2.xy *= rotate(PI * 0.5);
    q2.y -= 1.73;
    q2.x -= 2.11;
    hit.dist = min(hit.dist, sdCappedTorus(q2, vec2(1.0,-0.), 0.517, r));
    
    vec3 q3 = p;
    q3 += vec3(0.09, 0.05, 0.);
    q3.y += sin(q3.x * 5.0) * 0.2;
    hit.dist = min(hit.dist, sdCapsule(q3 + vec3(1.7, -1.44, 0.), vec3(0.12, 0.0, 0.), vec3(0.5, 0., 0.), r, 0.));
    
    vec3 q4 = p;
    q4.xy *= rotate(0.015);
    vec3 a = vec3(-1.32, 0.0, 0.0);
    vec3 b = vec3(1.31, 0.0, 0.0);
    float t = (q4.x - a.x) / (b.x - a.x);
    float power = 5.;
    if (t-0.5 > 0.) {
        power = 8.;
    }
    t = abs(t - 0.5) * 2.0;
    t = pow(t, power);
    hit.dist = min(hit.dist, sdCapsule(q4 + vec3(0.0, -1.10-t*0.3, 0.), a, b, r, 0.));
    
    vec3 q5 = p;
    q5.xy *= rotate(-0.01);
    a = vec3(1.22, 0.0, 0.0);
    b = vec3(1.9, 0.0, 0.0);
    t = (q5.x - a.x) / (b.x - a.x);
    power = 3.;
    if (t-0.5 < 0.18 && t-0.5 > -0.5) {
        t = abs(t - 0.5) * 2.0;
        t = pow(t, power);
        hit.dist = min(hit.dist, sdCapsule(q4 + vec3(0.0, -1.53+t*0.2, 0.), a, b, r, 0.));
    }
    
    vec3 q6 = p;
    q6.xy *= rotate(-PI * 0.59);
    q6.x += 2.53;
    q6.y += -1.24;
    q6.x *= 1.06;
    q6.y *= 0.985;
    hit.dist = min(hit.dist, sdCappedTorus(q6, vec2(1.0,-0.), 0.6, r));
    
    return hit;
}

Hit map_main_backing(vec3 p) {
    Hit hit;
    hit.type = HIT_TYPE_MAIN_BACKING;
    hit.emissive = false;
    vec3 q = p;
    q.yz *= rotate(-PI * 0.5);
    hit.dist = sdRoundedCylinder(q + vec3(1.76,0.125,-2.11), .62, 0.015, 0.08);
    hit.dist = min(hit.dist, sdRoundedCylinder(q + vec3(-1.99,0.1,-2.08), .65, 0.015, 0.08));
    float s = sin((p.x + 2.65)*0.6)*0.25;
    float x = -abs(p.x+0.02);
    x = x*x*x*x * x*x*x;
    s += x*0.025;
    if (p.y < 2.3) {
        hit.dist = min(hit.dist, sdRoundBox(p + vec3(0.0, -1.805 + s, -0.1), vec3(2.67, 0.8, 0.015), 0.3));
    }
    hit.dist = min(hit.dist, sdRoundBox(p + vec3(0.0, -2.43 + sin((p.x + 1.66)*0.93)*0.55, -0.1), vec3(1.2, 0.8, 0.015), 0.3));
    vec3 q2 = q;
    q2.xz *= rotate(-0.14);
    hit.dist = min(hit.dist, sdRoundedCylinder(q2 * vec3(0.44, 1.0, 1.0) + vec3(0.72,0.14,-2.19), .35, 0.015, 0.08));
    vec3 q3 = q;
    q3.xz *= rotate(0.155);
    hit.dist = min(hit.dist, sdRoundedCylinder(q3 * vec3(0.38, 1.0, 1.0) + vec3(-0.667,0.14,-2.18), .35, 0.015, 0.08));
    vec3 q4 = p;
    s = sin((q4.x+1.3)*3.45)*0.32;
    float t = 1.0;
    if (s > 0.0) {
        t = sin(1.-s)*0.8;
    }
    q4.y += t*s;
    q4 += vec3(1.3, -1.76, -0.1);
    hit.dist = min(hit.dist, sdRoundBox(q4, vec3(0.45,0.65,0.015), 0.1));
    vec3 q6 = p;
    q6 += vec3(-1.5, -1.88, -0.1);
    q6.y += sin((q6.x + 1.93)*5.2)*0.18;
    hit.dist = min(hit.dist, sdRoundBox(q6, vec3(0.35,0.65,0.015), 0.1));
    return hit;
}

Hit map_pole(vec3 p) {
    Hit hit;
    hit.type = HIT_TYPE_POLE;
    hit.emissive = false;
    vec3 q = p;
    q.y -= 0.05;
    float h = 3.6;
    float t = mix(1.0, 1.5, (q.y + h) / (2.0 * h));
    q.x *= t;
    q.z -= 0.3;
    hit.dist = sdRoundBox(q, vec3(0.12, h, 0.12), 0.02);
    p.y += 3.4;
    t = mix(1.0, 1.3, (p.y + 0.2) / (2.0 * 0.2));
    p.x *= t;
    p.z -= 0.28;
    float base_dist = sdRoundBox(p, vec3(0.27, 0.14, 0.27), 0.01);
    if (base_dist < hit.dist) {
        hit.type = HIT_TYPE_POLE_BASE;
        hit.dist = base_dist;
    }
    return hit;
}

Hit map(vec3 p) {
    Hit F = map_F(p);
    Hit U = map_U(p);
    Hit N = map_N(p);
    Hit C = map_C(p);
    Hit I = map_I(p);
    Hit T = map_T(p);
    Hit Y = map_Y(p);
    Hit MOTEL = map_MOTEL(p);
    Hit circle = map_circle(p);
    Hit circle_lights = map_circle_emitters(p);
    Hit arrow = map_arrow(p);
    Hit arrow_lights = map_arrow_lights(p);
    Hit main_backing = map_main_backing(p);
    Hit main_backing_lights = map_main_backing_lights(p);
    Hit main_backing_lights_2;
    float split_dist = abs(p.y - 2.04);
    if (split_dist > 0.02) {
        vec3 q = p;
        float power = 1.282;
        if (q.y > 2.04) { power = 1.18; }
        q.y = pow(q.y, power);
        main_backing_lights_2 = map_main_backing_lights(q*vec3(0.976 + split_dist * 0.03, 0.71, 1.0) + vec3(0.0, 0.356, 0.0));
    } else {
        main_backing_lights_2.dist = 999999.0;
    }
    Hit pole = map_pole(p);
    
    Hit min_hit = F;
    if (U.dist < min_hit.dist) { min_hit = U; }
    if (N.dist < min_hit.dist) { min_hit = N; }
    if (C.dist < min_hit.dist) { min_hit = C; }
    if (I.dist < min_hit.dist) { min_hit = I; }
    if (T.dist < min_hit.dist) { min_hit = T; }
    if (Y.dist < min_hit.dist) { min_hit = Y; }
    if (MOTEL.dist < min_hit.dist) { min_hit = MOTEL; }
    if (circle.dist < min_hit.dist) { min_hit = circle; }
    if (circle_lights.dist < min_hit.dist) { min_hit = circle_lights; }
    if (arrow.dist < min_hit.dist) { min_hit = arrow; }
    if (arrow_lights.dist < min_hit.dist) { min_hit = arrow_lights; }
    if (main_backing_lights.dist < min_hit.dist) { min_hit = main_backing_lights; }
    if (main_backing_lights_2.dist < min_hit.dist) { min_hit = main_backing_lights_2; }
    if (main_backing.dist < min_hit.dist) { min_hit = main_backing; }
    if (pole.dist < min_hit.dist) { min_hit = pole; }
    return min_hit;
}

Emitter_Hits push_emitter_hit(Emitter_Hits in, Hit h) {
    if (h.dist < in.dist1) {
        in.dist2 = in.dist1;
        in.type2 = in.type1;
        in.dist3 = in.dist2;
        in.type3 = in.type3;
        in.dist1 = h.dist;
        in.type1 = h.type;
    } else if (h.dist < in.dist2) {
        in.dist3 = in.dist2;
        in.type3 = in.type2;
        in.dist2 = h.dist;
        in.type2 = h.type;
    } else if (h.dist < in.dist3) {
        in.dist3 = h.dist;
        in.type3 = h.type;
    }
    return in;
}

Emitter_Hits map_emitters(vec3 p) {
    Hit F = map_F(p);
    Hit U = map_U(p);
    Hit N = map_N(p);
    Hit C = map_C(p);
    Hit I = map_I(p);
    Hit T = map_T(p);
    Hit Y = map_Y(p);
    Hit MOTEL = map_MOTEL(p);
    Hit circle = map_circle(p);
    Hit circle_lights = map_circle_emitters(p);
    Hit arrow_lights = map_arrow_lights(p);
    Hit main_backing_lights = map_main_backing_lights(p);
    Hit main_backing_lights_2;
    float split_dist = abs(p.y - 2.04);
    if (split_dist > 0.02) {
        vec3 q = p;
        float power = 1.282;
        if (q.y > 2.04) { power = 1.18; }
        q.y = pow(q.y, power);
        main_backing_lights_2 = map_main_backing_lights(q*vec3(0.976 + split_dist * 0.03, 0.71, 1.0) + vec3(0.0, 0.356, 0.0));
    } else {
        main_backing_lights_2.dist = 999999.0;
    }
    
    Emitter_Hits hits;
    hits.dist1 = 99999999.0;
    hits.dist2 = 99999999.0;
    hits.dist3 = 99999999.0;

    hits = push_emitter_hit(hits, F);
    hits = push_emitter_hit(hits, U);
    hits = push_emitter_hit(hits, N);
    hits = push_emitter_hit(hits, C);
    hits = push_emitter_hit(hits, I);
    hits = push_emitter_hit(hits, T);
    hits = push_emitter_hit(hits, Y);
    hits = push_emitter_hit(hits, arrow_lights);
    hits = push_emitter_hit(hits, main_backing_lights);
    hits = push_emitter_hit(hits, main_backing_lights_2);
    hits = push_emitter_hit(hits, MOTEL);
    hits = push_emitter_hit(hits, circle_lights);

    return hits;
}

vec4 get_hit_color(int type) {
    vec4 blue = vec4(0.1, 0.6, 0.99, 1.);
    vec4 green = vec4(0.25, 0.79, 0., 1.);
    vec4 red = vec4(0.99, 0.28, 0.34, 1.);
    vec4 yellow = vec4(0.9, 0.86, 0.0, 1.);
    float s = 0.7;
    if (type == HIT_TYPE_F) { return vec4(green.xyz * s, 1.0); }
    else if (type == HIT_TYPE_U) { return vec4(red.xyz * s, 1.0); }
    else if (type == HIT_TYPE_N) { return vec4(blue.xyz * s, 1.0); }
    else if (type == HIT_TYPE_C) { return vec4(green.xyz * s, 1.0); }
    else if (type == HIT_TYPE_I) { return vec4(red.xyz * s, 1.0); }
    else if (type == HIT_TYPE_T) { return vec4(blue.xyz * s, 1.0); }
    else if (type == HIT_TYPE_Y) { return vec4(green.xyz * s, 1.0); }
    else if (type == HIT_TYPE_MOTEL) { return vec4(yellow.xyz, 1.0); }
    else if (type == HIT_TYPE_MOTEL_BACKING) { return vec4(blue.xyz * 0.3, 1.0); }
    else if (type == HIT_TYPE_CIRCLE_SPHERE) { return yellow; }
    else if (type == HIT_TYPE_CIRCLE_BACK_OUTER) { return vec4(yellow.xyz * 0.4, 1.0); }
    else if (type == HIT_TYPE_CIRCLE_BACK_INNER) { return vec4(blue.xyz * 0.4, 1.0); }
    else if (type == HIT_TYPE_CIRCLE_BAR) { return vec4(0.95, 0.96, 1.0, 1.); }
    else if (type == HIT_TYPE_ARROW) { return vec4(yellow.xyz * s, 1.0); }
    else if (type == HIT_TYPE_ARROW_SPHERE) { return vec4(yellow.xyz, 1.0); }
    else if (type == HIT_TYPE_MAIN_BACKING) { return vec4(blue.xyz * 0.2, 1.0); }
    else if (type == HIT_TYPE_POLE) { return vec4(0.085, 0.08, 0.08, 1.); }
    else if (type == HIT_TYPE_POLE_BASE) { return vec4(0.2, 0.2, 0.2, 1.); }
    else { return vec4(1.,1.,1.,1.); }
}

void main()
{
    vec2 uv = (gl_FragCoord.xy/iResolution.xy-.5) * vec2(iResolution.x/iResolution.y, 1.);
    vec3 ro = vec3(0.,-1.,-8.0);
    vec3 rd = normalize(vec3(uv.x, uv.y + 0.15, 1.0));

    rd.xz *= rotate(PI * 0.18);
    ro.xz *= rotate(PI * 0.18);

    float t = 0.;
    vec3 col = vec3(0.0,0.0,0.0);
    fragColor = vec4(col.xyz, 1.);
    float alpha = 1.;
    bool was_hit = false;
    vec4 hit_color = vec4(1.,1.,1.,1.);
    for (int i = 0; i < MAX_STEPS; i++) {
        vec3 p = ro + rd * t;
        Hit hit = map(p);
        if (abs(hit.dist) <= MIN_HIT_DIST) {
            float weight = 0.15;
            if (hit.type == HIT_TYPE_CIRCLE_BACK_INNER || hit.type == HIT_TYPE_CIRCLE_BACK_OUTER) weight = 0.5;
            col = get_hit_color(hit.type).xyz * weight;
            if (hit.type == HIT_TYPE_POLE || hit.type == HIT_TYPE_POLE_BASE) {
                col = get_hit_color(hit.type).xyz + vec3(0.9, 0.86, 0.0) * (1.0 / (1.0 + abs(p.y)*abs(p.y))) * 0.7;
            }
            break;
        }
        t += hit.dist;
    }

    // Volumetrics
    
    int VOLUMETRICS_MAX_STEPS = 32;
    t = 0.0;
    for (int i = 0; i < VOLUMETRICS_MAX_STEPS; i++) {
        vec3 p = ro + rd * t;
        float a = float(iFrame + 1) + float(i + 1);
        vec3 rand_dir = vec3(hash3(a * (ro + p)), hash2(a * (uv.xy + p.xz)), hash2(a * (rd.xy + p.xy)));
        rand_dir.xy -= 0.5;
        rand_dir = normalize(rand_dir);
        Emitter_Hits hits = map_emitters(p + rand_dir * 0.005);
        float max_dist = 0.5;
        float weight = hash3(a * (ro + p));
        float s = 350.0;
        float dist = abs(hits.dist1);
        if (dist > MIN_HIT_DIST-0.0005) {
            float w = weight * (max_dist - dist) / max_dist;
            col.xyz += get_hit_color(hits.type1).xyz * (1.0 / (1.0 + dist*dist*s)) * weight;
            dist = abs(hits.dist2);
            if (dist > MIN_HIT_DIST) {
                w = weight * (max_dist - dist) / max_dist;
                col.xyz += get_hit_color(hits.type2).xyz * (1.0 / (1.0 + dist*dist*s)) * weight;
            }
            t += hits.dist1;
        } else {
            break;
        }
    }
    float exposure = 1.5;
    col.xyz *= exposure;
    col.xyz = col.xyz / (1.0 + col.xyz);
    fragColor = vec4(col.xyz, 1.0);
}