#ifdef GL_ES
precision mediump float;
#endif

uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;

#define kPI05    1.5707963267949
#define kPI      3.1415926535898
#define kPI2     6.2831853071796

//
vec3 rotZ(vec3 p, float a) {
   vec2 sc = sin(vec2(a, a + kPI05));
   return vec3(
       p.x * sc.y + p.y * -sc.x,
       p.x * sc.x + p.y * sc.y,
       p.z
   );
}

vec3 lookAt(vec3 o, vec3 t, vec3 u, vec3 d) {
   vec3 f = normalize(o - t);
   vec3 s = normalize(cross(u, f));
   vec3 h = normalize(cross(f, s));
   return vec3(
       dot(vec3(s.x, h.x, f.x), d),
       dot(vec3(s.y, h.y, f.y), d),
       dot(vec3(s.z, h.z, f.z), d));
}

// Hash without Sine https://www.shadertoy.com/view/4djSRW
float hash11(float p) {
   p = fract(p * .1031);
   p *= p + 33.33;
   p *= p + p;
   return fract(p);
}
float hash13(vec3 p3) {
   p3  = fract(p3 * .1031);
   p3 += dot(p3, p3.yzx + 33.33);
   return fract((p3.x + p3.y) * p3.z);
}
vec2 hash22(vec2 p) {
   vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973));
   p3 += dot(p3, p3.yzx+33.33);
   return fract((p3.xx+p3.yz)*p3.zy);
}
vec3 hash33(vec3 p3) {
   p3 = fract(p3 * vec3(.1031, .1030, .0973));
   p3 += dot(p3, p3.yxz+33.33);
   return fract((p3.xxy + p3.yxx)*p3.zyx);
}

//
vec3 voro3(vec3 p, vec3 r) {
   vec3 ret;
   vec3 ip = floor(p);
   const int N = 1;
   float lmax = 10.0;
   vec3 minip;
   vec3 mintp;

   for(int iz = -N; iz <= N; iz++) {
       for(int iy = -N; iy <= N; iy++) {
           for(int ix = -N; ix <= N; ix++) {
               vec3 tp = ip + vec3(ivec3(ix, iy, iz));
               tp += (hash33(tp) - vec3(0.5)) * r;
               float l = length(tp - p);
               if(l < lmax) {
                   lmax = l;
                   minip = ip + vec3(ivec3(ix, iy, iz));
                   mintp = tp;
               }
           }
       }
   }
   ret.x = lmax;
   ret.z = hash13(minip);

   lmax = 10.0;
   vec3 minvp;
   for(int iz = -N; iz <= N; iz++) {
       for(int iy = -N; iy <= N; iy++) {
           for(int ix = -N; ix <= N; ix++) {
               vec3 tp = minip + vec3(ivec3(ix, iy, iz));
               tp += (hash33(tp) - vec3(0.5)) * r;
               vec3 vp = tp - mintp;
               float l = length(vp);
               if(l > 0.0) {
                   vec3 mp = (tp + mintp) * 0.5;
                   l = abs(dot(p - mp, vp / l));
                   if(l < lmax) {
                       lmax = l;
                       minvp = vp;
                   }
               }
           }
       }
   }
   ret.y = lmax;
   return ret;
}

float star(vec2 p) {
   float a = atan(p.y, p.x) / kPI2 + 0.5;
   a = abs(fract(a * 5.0) * 2.0 - 1.0) * kPI2 / 10.0;
   float r = length(p);
   vec2 q = cos(vec2(a, a - kPI05)) * r;
   float s = kPI / 180.0 * 55.0;
   vec2 n = cos(vec2(s, s - kPI05));
   return 0.5 - dot(q, n);
}

float flower(vec2 p, float w) {
   vec2 tt = vec2(1e2);
   for(float i = 0.0; i < 5.0; i+=1.0) {
       float a = kPI2 * i / 5.0;
       vec2 o = cos(vec2(a, a - kPI05));
       float to = length(p - o);
       tt = min(tt, vec2(to, abs(to - 1.1756)));
   }
   return w - (length(p) > 1.0 ? tt.x : tt.y);
}

//
float smin(float a, float b, float k) {
   k *= 4.0;
   float h = max(k - abs(a - b), 0.0) / k;
   return min(a, b) - h * h * k * 0.25;
}

float sphere(vec3 p, vec3 o, float r) {
   return length(p - o) - r;
}

vec2 tsphere(vec3 ro, vec3 rd, vec3 sc, float sr, out vec3 ret0, out vec3 ret1) {
   vec3 oc = ro - sc;
   float a = dot(rd, rd);
   float b = 2.0 * dot(rd, oc);
   float c = dot(oc, oc) - sr * sr;
   float d = b * b - 4.0 * a * c;
   if(d < 0.0) return vec2(-1.0);
   vec2 t = (vec2(-b) + vec2(-1.0, 1.0) * sqrt(d)) * 0.5 / a;
   ret0 = rd * t.x + ro;
   ret1 = rd * t.y + ro;
   return t;
}

float tplane(vec3 o, vec3 d, vec3 n, vec3 p, out vec3 ret) {
   vec3 po = p - o;
   float nd = dot(n, d);
   float t = dot(n, po) / nd;
   if(t < 0.0 || abs(nd) <= 1e-8) return -1.0;
   ret = d * t + o;
   return t;
}

//
vec3 bg(vec3 p) {
   vec3 d = normalize(p);
   vec3 c = vec3(0.0, 0.0, 0.02);

   vec3 vr = voro3(d * 120.0, vec3(1.0));
   float f = 10.0 + abs(sin((vr.z * 24.0 + time) * 2.0)) * 20.0;
   vec3 s = mix(vec3(1.0, 0.6, 0.4), vec3(0.4, 0.9, 1.0), fract(vr.z * vr.z * 1e3));
   c += pow(vec3(1.0 - sqrt(vr.x)), vec3(10.0)) * f * s;

   vec4 hc = mix(vec4(0.1, 0.15, 0.05, 25.0),
                   vec4(0.0, 0.2, 0.8, 50.0),
                   smoothstep(-0.03, 0.03, d.y));
   c += pow(1.0-abs(d.y), hc.w) * hc.rgb;
   c += exp(d.y * -d.y * 3e4) * pow(-d.z, 12.0) * vec3(1.0);

   return c;
}

// scene
const float kDz = 4.0;
const float kIntro = 8.0;
const float kSeq = 16.0;

vec2 i2p(vec2 p, float i, inout float y) {
   if(i > kIntro) {
       float iseq = mod(i - kIntro, kSeq);
       if(iseq == 0.0) {
           p = vec2(0.0, -1.0);
           y *= 2.0;
       } else if(iseq > kSeq - 4.0) {
           p = vec2(p.x * 0.1, 2.0 - (kSeq - iseq));
       }
   }
   return p;
}

vec2 pos2(float t) {
   float i = floor(t);
   float f = fract(t);

   vec2 xx = vec2(-1.0, 1.0) * (fract(i * 0.5) * 4.0 - 1.0);
   float y = 4.0 * f * (1.0 - f);

   vec2 o0 = hash22(vec2(i) + vec2(0.0, 0.3456789));
   vec2 o1 = hash22(vec2(i + 1.0) + vec2(0.0, 0.3456789));

   o0 = o0 * 2.0 - vec2(1.0);
   o1 = o1 * 2.0 - vec2(1.0);

   vec2 p0 = vec2(xx.x, 0.0) + o0;
   vec2 p1 = vec2(xx.y, 0.0) + o1;

   p0 = i2p(p0, i, y);
   p1 = i2p(p1, i + 1.0, y);

   return mix(p0, p1, f) + vec2(0.0, y);
}

float map(vec3 p, vec3 tbif) {
   float tb = tbif.x;
   float ti = tbif.y;
   float tf = tbif.z;

   const float N = 16.0;
   const float r0 = 0.01;
   float d = sphere(p, vec3(pos2(tb), 0.0), r0);
   float dt = 0.0;
   for(float i = 1.0; i < N; i+=1.0) {
       float f = sqrt(i / (N - 1.0));
       f = 4.0 * f * (1.0 - f);
       dt += mix(0.02, 0.05, f);
       vec2 sp = pos2(tb - dt);
       float r = 0.1 * f + r0;
       float s = mix(0.03, 0.05, f);
       d = smin(d, sphere(p, vec3(sp, dt * kDz), r), s);
   }
   return d;
}

vec3 normal(vec3 p, vec3 t) {
   int id;
   vec2 e = vec2(1.0, -1.0) * 0.5773;
   const float eps = 0.00025;
   return normalize( e.xyy * map(p + e.xyy * eps, t) +
                                         e.yyx * map(p + e.yyx * eps, t) +
                                         e.yxy * map(p + e.yxy * eps, t) +
                                         e.xxx * map(p + e.xxx * eps, t) );
}

vec3 fxs(vec3 ro, vec3 rd, float maxt, vec3 tbif) {
   float tb = tbif.x;
   float ti = tbif.y;
   float tf = tbif.z;

   vec3 ret = vec3(0.0, 0.0, 0.0);

   for(float i = 0.0; i < 4.0; i+=1.0) {
       float pti = ti - i;
       if(pti < 0.0) break;

       float ft = (tb - pti);
       vec3 fp = vec3(pos2(pti), ft * kDz);
       if(pti < kIntro) maxt = 100.0;

       {
           vec3 hp;
           float ht = tplane(ro, rd, vec3(0.0, 1.0, 0.0), fp, hp);
           if(ht >= 0.0 && ht < maxt) {
               float r = ft * 1.2;
               float t = length(fp - hp);
               float f = smoothstep(0.95, 1.0, 1.0 - abs(t - r));
               f *= max(0.0, 1.0 - t * t * 1.0);
               ret += vec3(1.0, 1.0, 1.0) * f;
           }
       }

       if(pti < kIntro) continue;
       float scl = (mod(pti - kIntro, kSeq) == 0.0) ? 4.0 : 1.0;
       scl = (pti == kIntro) ? 1.0 : scl;

       for(float k = 0.0; k < 2.0; k+=1.0) {
           float fd = 1.0 - smoothstep(1.0, 1.5, ft);
           float sr = (0.5 * ft * ft + 1e-1) * (1.0 + k * 2.0) * scl;
           vec3 sp = fp + vec3(0.0, sr, 0.0);
           vec3 shp0, shp1;
           vec2 sht = tsphere(ro, rd, sp, sr, shp0, shp1);
           for(int j = 0; j < 2; j++) {
               float st = (j == 0) ? sht.x : sht.y;
               if(st < 0.0 || st > maxt) continue;
               vec3 lp = rd * st + ro - sp;

               float a = atan(lp.z, lp.x) + pti * 0.7 + k * 0.6;
               float r = acos(clamp(-lp.y / sr, -1.0, 1.0)) / kPI;

               r *= max(1.0, sr * 5.0 / scl);
               vec2 uv = cos(vec2(a, a - kPI05)) * r;
               // ret += vec3(fract(length(uv)));
               float tx = flower(uv, 0.04);
               ret += vec3(4.0, 3.0, 2.0) * smoothstep(-0.02, 0.04, tx) * fd;
           }
       }

       float strrng = fract(sin(pti + 1.7654321));
       for(float k = 0.0; k < 20.0; k+= 1.0) {
           strrng = fract(strrng * (1.23456 + strrng) * 100.0);
           float rnd = strrng;
           float a = (k + pti) * 2.4 + rnd;
           vec3 pp = fp;
           pp.xz += cos(vec2(a, a - kPI05)) * ft * (0.4 + rnd * 0.8) * scl;
           pp.y += ft * (0.9 + rnd * 0.5 - ft) * 2.0 * scl;

           vec3 hp;
           float ht = tplane(ro, rd, vec3(0.0, 0.0, -1.0), pp, hp);
           if(ht >= 0.0 && ht < maxt) {
               vec2 uv = (hp.xy - pp.xy) * 12.0 / scl;
               uv = rotZ(uv.xyy, k * 2.4).xy;
               float tx = star(uv);
               float fd = 1.0 - smoothstep(1.0, 1.5, ft);
               ret += vec3(1.5, 1.0, 0.6) * fd * smoothstep(-0.1, 0.1, tx);
           }
       }
   }

   return ret;
}

void main() {
   vec3 t;
   t.x = time * 120.0 / 60.0;
   t.y = floor(t.x);
   t.z = fract(t.x);

   vec3 color = vec3(0.0);
   vec2 sc = (gl_FragCoord.xy * 2.0 - resolution.xy) / min(resolution.x, resolution.y);

   vec3 ro = vec3(0.0, 0.0, 10.0);
   vec3 rd = normalize(vec3(sc.xy, -4.0));
   ro.xy += vec2(0.0, 0.4 + sin(time * 1.3) * 0.03) + sin(vec2(time) * vec2(0.7, 1.15)) * 0.04;
   vec3 lp = vec3(pos2(t.x - 0.6) * vec2(0.02, 0.002), sin(time * 0.2) * 0.05);
   rd = lookAt(ro, lp, vec3(0.0, 1.0, 0.0), rd);

   color = bg(rd);

   float td = 0.0;
   float blm = 0.0;
   for(int i = 0; i < 64; i++) {
       vec3 rp = rd * td + ro;
       float d = map(rp, t);
       if(d < 1e-2) {
           vec3 nv = normal(rp, t);
           color = mix(vec3(0.4, 0.4, 0.55), vec3(0.8), (nv.y * 0.5 + 0.5));
           color += vec3(0.2, 0.3, 0.0) * (1.0 - abs(nv.y));
           break;
       }
       blm += pow(max(0.0, 1.0 - d * 8.0), 10.0);
       td += d;
   }
   color += vec3(0.1, 0.2, 0.2) * blm;

   if(t.y < kIntro) {
       color = vec3(0.0);
   } else {
       color += max(0.0, 1.0 - (t.x - kIntro) * 2.0);
   }

   color += fxs(ro, rd, td, t);
   gl_FragColor = vec4(pow(color, vec3(1.0/2.2)), 1.0);
}