// BEGIN: shadertoy porting template
// https://gam0022.net/blog/2019/03/04/porting-from-shadertoy-to-glslsandbox/
precision highp float;

uniform vec2 resolution;
uniform float time;
uniform vec2 mouse;
uniform sampler2D backbuffer;

#define iResolution resolution
#define iTime time
#define iMouse (vec4(mouse, .5, .5) * resolution.xyxy)
#define iChannel0 backbuffer

void mainImage(out vec4 fragColor, vec2 fragCoord);
out vec4 outColor;
void main () {
   vec4 col;
   mainImage(col, gl_FragCoord.xy);
   outColor = col;
}
// END: shadertoy porting template

const float TAU = 6.28318530718;
#define BPM 120.0
#define saturate(x) clamp(x, 0., 1.)
#define tri(x) (1. - 4. * abs(fract(x) - .5))
#define phase(x) (floor(x) + .5 + .5 * cos(TAU * .5 * exp(-5. * fract(x))))
void rot(inout vec2 p, float a) { p *= mat2(cos(a), sin(a), -sin(a), cos(a)); }

// Hash without Sine by David Hoskins.
// 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 hash12(vec2 p) {
   vec3 p3 = fract(vec3(p.xyx) * .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);
}

vec2 hash23(vec3 p3) {
   p3 = fract(p3 * vec3(.1031, .1030, .0973));
   p3 += dot(p3, p3.yzx + 33.33);
   return fract((p3.xx + p3.yz) * p3.zy);
}

// hemisphere hash function based on a hash by Slerpy
vec3 hashHs(vec3 n, vec3 seed) {
   vec2 h = hash23(seed);
   float a = h.x * 2. - 1.;
   float b = TAU * h.y * 2. - 1.;
   float c = sqrt(1. - a * a);
   vec3 r = vec3(c * cos(b), a, c * sin(b));
   return r;
}

// global vars
vec3 ro, target;
float fov;
vec3 scol;
float beat, beatTau, beatPhase;

// Timeline
float prevEndTime = 0., t = 0.;
#define TL(end) if (t = beat - prevEndTime, beat < (prevEndTime = end))

// Material Types
#define VOL 0.
#define SOL 1.

vec2 opRep(vec2 p, vec2 a) { return mod(p, a) - 0.5 * a; }

void opUnion(inout vec4 m, float d, float type, float roughness_or_emissive, float hue) {
   if (d < m.x) m = vec4(d, type, roughness_or_emissive, hue);
}

vec3 pal(vec4 m) {
   // Integer part: Blend ratio with white (0-10)
   // Decimal part: Hue (0-1)
   vec3 col = vec3(.5) + .5 * cos(TAU * (vec3(0., .33, .67) + m.w));
   return mix(col, vec3(.5), .1 * floor(m.w));
}

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

// マンハッタン距離によるボロノイ
// https://qiita.com/7CIT/items/4126d23ffb1b28b80f27
// https://neort.io/art/br0fmis3p9f48fkiuk50
float voronoi(vec2 uv) {
   vec2 i = floor(uv);
   vec2 f = fract(uv);
   vec2 res = vec2(8, 8);

   for (int x = -1; x <= 1; x++) {
       for (int y = -1; y <= 1; y++) {
           vec2 n = vec2(x, y);
           vec2 np = 0.5 + 0.5 * sin((beatPhase / 4. + hash22(i + n)) * TAU);
           vec2 p = n + np - f;

           // マンハッタン距離
           float d = abs(p.x) + abs(p.y);
           // float d = length(p);
           // float d = lpnorm(p, -3);

           if (d < res.x) {
               res.y = res.x;
               res.x = d;
           } else if (d < res.y) {
               res.y = d;
           }
       }
   }

   float c = res.y - res.x;
   c = sqrt(c);
   c = smoothstep(0.4, 0.0, c);
   return c;
}

vec4 map(vec3 pos, bool isFull) {
   vec4 m = vec4(2, VOL, 0, 0);
   // x: Distance
   // y: MaterialType (VOL or SOL)
   // z: Roughness in (0-1), Emissive when z>1
   // w: ColorPalette

   float roughness = 0.05;

   vec3 p1 = pos;

   int _IFS_Iteration = 3;
   vec3 _IFS_Rot = vec3(0, 0.15, -0.25);
   vec3 _IFS_Offset = vec3(3, 4, 12);

   p1 -= _IFS_Offset.xyz;

   for (int i = 0; i < _IFS_Iteration; i++) {
       p1 = abs(p1 + _IFS_Offset.xyz) - _IFS_Offset.xyz;
       rot(p1.xz, TAU * _IFS_Rot.x);
       rot(p1.zy, TAU * _IFS_Rot.y);
       rot(p1.xy, TAU * _IFS_Rot.z);
   }

   float power = (beat >= 32. && beat < 64.) ? 100.0 : 1.0;
   float emi = 1.2 * pow(saturate(cos((beatTau - pos.y * 2.) / 8.)), power);
   float hue = fract(beat / 16.);

   vec3 size = vec3(4, 0.1, 4);
   opUnion(m, sdBox(p1, size) + voronoi(p1.xz), SOL, roughness, 0.);
   opUnion(m, sdBox(p1 - vec3(0, -0.2, 0), size), SOL, roughness + emi, hue);

   // wall
   emi = pow(saturate(cos(TAU * p1.x * 0.5)), 50.) * saturate(cos((beatTau - pos.y * 2.) / 8.));
   hue = 3.4;
   opUnion(m, sdBox(p1 - vec3(0, 4, 0), size), SOL, emi * 2., hue);

   return m;
}

vec3 normal(vec3 p) {
   vec2 e = vec2(0, .05);
   return normalize(map(p, false).x - vec3(map(p - e.yxx, false).x, map(p - e.xyx, false).x, map(p - e.xxy, false).x));
}

// Based on EOT - Grid scene by Virgill
// https://www.shadertoy.com/view/Xt3cWS
void madtracer(vec3 ro1, vec3 rd1, float seed) {
   scol = vec3(0);
   vec2 rand = hash23(vec3(seed, iTime, iTime)) * .5;
   float t = rand.x, t2 = rand.y;
   vec4 m1, m2;
   vec3 rd2, ro2, nor2;
   for (int i = 0; i < 100; i++) {
       m1 = map(ro1 + rd1 * t, true);
       // t += m1.y == VOL ? 0.25 * abs(m1.x) + 0.0008 : 0.25 * m1.x;
       t += 0.5 * mix(abs(m1.x) + 0.0032, m1.x, m1.y);
       ro2 = ro1 + rd1 * t;
       nor2 = normal(ro2);
       rd2 = mix(reflect(rd1, nor2), hashHs(nor2, vec3(seed, i, iTime)), saturate(m1.z));
       m2 = map(ro2 + rd2 * t2, true);
       // t2 += m2.y == VOL ? 0.15 * abs(m2.x) : 0.15 * m2.x;
       t2 += 0.25 * mix(abs(m2.x), m2.x, m2.y);
       scol += .15 * (pal(m2) * max(0., m2.z - 1.) + pal(m1) * max(0., m1.z - 1.));

       // force disable unroll for WebGL 1.0
       if (t < -1.) break;
   }
}

void raymarching(vec3 ro1, vec3 rd1) {
   scol = vec3(0);
   float t = 0.;
   vec4 m;
   for (int i = 0; i < 160; i++) {
       vec3 p = ro1 + rd1 * t;
       m = map(p, true);
       t += m.x;

       if (m.x < 0.01) {
           vec3 light = normalize(vec3(1, 1, -1));
           vec3 albedo = vec3(0.3);
           if (m.z > 1.) albedo = pal(m);
           scol = albedo * (0.5 + 0.5 * saturate(dot(normal(p), light)));
           break;
       }
   }
}

void mainImage(out vec4 fragColor, vec2 fragCoord) {
   beat = iTime * BPM / 60.0;
   // beat = 48.;
   beat = mod(beat, 96.0);
   beatTau = beat * TAU;
   beatPhase = phase(beat / 2.);

   vec2 uv = fragCoord.xy / iResolution.xy;

   // Camera
   vec2 noise = hash23(vec3(iTime, fragCoord)) - .5;  // AA
   vec2 uv2 = (2. * (fragCoord.xy + noise) - iResolution.xy) / iResolution.x;

   // Timeline
   TL(16.) {
       vec3 a = vec3(0, 0.1, 0.01) * t;
       ro = vec3(3.685370226301841, -4.959968195098165, -20.681291773889914) + a;
       target = vec3(0, 0, 0) + a;
       fov = 38.;
   }
   else TL(32.) {
       vec3 a = vec3(0, 0, 0.01) * t;
       ro = vec3(0., 11.945982556636304, 38.08763743207477) + a;
       target = vec3(0, 0, 0) + a;
       fov = 38.;
   }
   else TL(64.) {
       vec3 a = vec3(0, 0, 0.2) * t;
       ro = vec3(0, 9.715572757794958e-16, 15.866734416093387) + a;
       target = vec3(0, 0, 0) + a;
       fov = 38. + t;
   }
   else TL(80.) {
       vec3 a = vec3(0, 0.1, 0.01) * t;
       ro = vec3(-1.3462260362305196, -8.261048814107882, -28.966739530232058) + a;
       target = vec3(1.593920748030086, -0.030320796976565673, -0.9344052773004179) + a;
       fov = 38.;
   }
   else TL(96.) {
       vec3 a = vec3(0, -1, 0.01) * t;
       ro = vec3(0., -63.37835217641502, -0.414008392856417) + a;
       target = vec3(0, 0, 0) + a;
       fov = 38.;
   }

// #define DEBUG_CAMERA
#ifdef DEBUG_CAMERA
   if (gCameraDebug > 0.) {
       ro = vec3(gCameraEyeX, gCameraEyeY, gCameraEyeZ);
       target = vec3(gCameraTargetX, gCameraTargetY, gCameraTargetZ);
       fov = gCameraFov;
   }
#endif

   vec3 up = vec3(0, 1, 0);
   vec3 fwd = normalize(target - ro);
   vec3 right = normalize(cross(up, fwd));
   up = normalize(cross(fwd, right));
   vec3 rd = normalize(right * uv2.x + up * uv2.y + fwd / tan(fov * TAU / 720.));

// #define DEBUG_SCENE
#ifdef DEBUG_SCENE
   raymarching(ro, rd);
   fragColor = vec4(scol, 1.);
#else
   madtracer(ro, rd, hash12(uv2));
   vec3 bufa = texture(iChannel0, uv).xyz;

   // fade out
   // scol = mix(scol, vec3(0), smoothstep(92., 96., beat));
   fragColor = saturate(vec4(0.7 * scol + 0.7 * bufa, 1.));
#endif
}