// 1. goto https://glslsandbox.com/e
// 2. paste this code
// reflected structure by musk for TDF 2021

#extension GL_OES_standard_derivatives : enable

precision highp float;

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

vec3 col_1, col_2;

mat2 rotate(float a){
       float c=cos(a), s=sin(a);
       return mat2(
               +c, +s,
               -s, +c
       );
}

float ss_noise(float seed){
       vec2 s = gl_FragCoord.xy;
       return fract(
               (sin(dot(s,s)*0.2531+dot(s.xy,s.yx)*0.31343+dot(s,vec2(31.541+fract(seed*50.1191),54.932-fract(seed*30.1191)))-fract(seed*1.7191)))*765.4567
       );
}

vec3 sphere_noise(float seed){
       vec3 n;
       for (int i=0; i<3; i++){
               n = (vec3(ss_noise(seed+.93), ss_noise(seed+.31), ss_noise(seed+.56))-vec3(.5))*2.0;
               if (length(n) < 1.0) {
                       return n;
               }
       }
       return n;
}

float df(vec3 p)//distance function
{
       //float d=1.0-abs(p.y);

       float t = time;
       p.x+=t*0.07;
       p.z+=t*.03;

               float s = length(p);

       p.y=abs(p.y);

       p.y-=5.0;

       float d = 0.5-p.y;

       for (int i=0; i<10; i++)
       {
               float fi = float(i);

               p+=vec3(1.25-fi,0.0,1.75+fi);
               vec3 pm;

               float rep = 10.0+sin(fi*2.0+1.0)*4.0;

               pm.xz = mod(p.xz+vec2(rep*.5),vec2(rep))-vec2(rep*.5);

               float width = 1.0+sin(fi)*.8;
               float height = 2.0+cos(fi)*1.1;
               float offset = -0.5+cos(fi)*1.8;

               vec3 df = abs(vec3(pm.x,p.y+1.0/width,pm.z))-vec3(width,height,width);
               float box = max(max(df.x,df.y),df.z);
               //float box=length(vec3(pm.x,p.y,pm.z))-1.0;

               d = min(d,box);
       }

       return d;
}

vec3 nf(vec3 p, float d){
       vec2 e = vec2(0,d);
       return normalize(vec3(
               df(vec3(p+e.yxx))-df(vec3(p-e.yxx)),
               df(vec3(p+e.xyx))-df(vec3(p-e.xyx)),
               df(vec3(p+e.xxy))-df(vec3(p-e.xxy))
       ));
}

float pat(vec3 p){
       float q = 2.0*(abs(fract(df(p+vec3(3.))*13.0)-.5));
       float dq2 = df(p-vec3(1))*.1;
       float q2 = smoothstep(.2,.3, (abs(fract(dq2*32.0)-0.5)*2.0));
       float q3 = smoothstep(.2,.3, (abs(fract(dq2*16.0)-0.5)*2.0));
       float q4 = smoothstep(.2,.3, (abs(fract(dq2*16.0)-0.5)*2.0));
       return smoothstep(.7,.8,q)*q2*q3;
}

float fat_pat(vec3 p){
       float q = 2.0*(abs(fract(df(p+vec3(3.))*8.0)-.5));
       float dq2 = df(p-vec3(1))*.1;
       float q2 = smoothstep(.2,.3, (abs(fract(dq2*32.0)-0.5)*2.0));
       float q3 = smoothstep(.2,.3, (abs(fract(dq2*16.0)-0.5)*2.0));
       float q4 = smoothstep(.2,.3, (abs(fract(dq2*16.0)-0.5)*2.0));
       return smoothstep(-.1,.6,q)*q2*q3;
}

float wave(float x){
       return max(.0, (1./(3.+sin(x)+sin(x*2.)))-.3);
}

vec3 ef(vec3 p){
       float pt = pat(p);
       return max(vec3(.0), pt*col_1*20.0*(wave((p.x+p.z)*.6+time)))
               +max(vec3(.0), pt*col_2*20.0*(wave((p.x-p.z)*.125+time)));
}

vec3 fat_ef(vec3 p){
       float pt = fat_pat(p);
       return max(vec3(.0), pt*col_1*20.0*(sin((p.x+p.z)*.5+time)-.5))
               +max(vec3(.0), pt*col_2*20.0*(sin((p.x-p.z)*.125+time)-.5));
}

vec3 decode_color(vec3 frag){
       return frag.xyz / (vec3(1.431)-frag);
}

vec3 encode_color(vec3 color){
       return (vec3(1.431)*color)/(vec3(1)+color);
}

void main( void ) {

       col_1 = mix( vec3(.9,.1,.1), vec3(.2,.5,.9), smoothstep(-.5,.5,sin(time*.167)) );
       col_2 = mix( vec3(.7,.3,.1), vec3(.9,.9,.9), smoothstep(-.5,.5,sin(time*.134)) );

       vec2 uv = gl_FragCoord.xy / resolution.xy;
       vec2 nuv = (uv-0.5)*2.0;

       vec3 color = vec3(0.0);
       float it_count, dist = ss_noise(time)*.1;
       vec3 pos = vec3(0,0,4);
       vec2 smpn = vec2(ss_noise(time+1.),ss_noise(time+2.));
       vec3 dir = normalize(vec3((gl_FragCoord.xy+smpn-resolution.xy*.5)/resolution.yy, -0.4));
       dir.z += pow(length(dir),2.0)*0.4;
       dir = normalize(dir);
       dir.xy *= rotate(.1);
       dir.xz *= rotate(+time*.025);
       pos.xz *= rotate(-time*.025);

       for (int it=0; it<100; it+=1){
               float d = df(pos+dist*dir);
               dist += d;
               if (d<1e-3) break;
               it_count = float(it);
       }
       vec3 pos_2 = pos+dist*dir;

       vec3 norm_outline = nf(pos_2, 48.0*dist/resolution.y);
       vec3 norm = nf(pos_2, 2.0*dist/resolution.y);

       vec3 em_2 = ef(pos_2);
       float pat_2 = pat(pos_2);
       float refl_2 = mix(pow(clamp(1.0+dot(norm, dir),.0,1.),4.0),1.0,0.2);

       // specular
       vec3 dir_2 = normalize(reflect(dir, norm + max(.0,.005-pat_2)*sphere_noise(time+93.1)));
       float dist_2 = ss_noise(time+.43)*.1+.1;
       for (int it=0; it<50; it++){
               float d = df(pos_2+dist_2*dir_2);
               dist_2 += d;
               if (d<1e-3) break;
       }
       vec3 pos_3 = pos_2+dist_2*dir_2;
       vec3 em_3 = ef(pos_3);

       vec3 em = em_2;
       vec3 specular = em_3;

       // diffuse
       float diffuse_count = .0;
       vec3 diffuse_color = vec3(.0);
       for (int dic=0; dic<10; dic++){
               vec3 d_dir=normalize(norm + .75*sphere_noise(1.0+dot(pos_2,diffuse_color)-time+diffuse_count));
               float d_dist = (1.0/dot(norm, d_dir))*.005;
               for (int dit=0; dit<10; dit++){
                       float d = df(pos_2+d_dir*d_dist);
                       d_dist += d;
                       if (d<1e-3) break;
               }
               diffuse_count += 1.0;
               diffuse_color += ef(pos_2+d_dir*d_dist);
       }
       diffuse_color *= mix(1.0, .5,pat_2)/diffuse_count;

       color = mix(diffuse_color +mix(em_2, em_2*vec3(.8,.6,.4), pat_2), specular, refl_2);
       // color = vec3(refl_2);
       color *= (1.0-1.4*length(uv-vec2(0.5)))*2.0;

       // color = diffuse_color; //dbg
       // color = vec3(pat_2); //dbg
       // color = vec3(vec3(0.1,.2,.3)*it_count*0.4); //dbg maytbe


       float out_dist = .01;
       float outline = 1.0-clamp((1.0-df(pos_2+norm_outline*out_dist)/out_dist)*4.0, 0.0, 1.0);
       outline = mix(outline, 1.0, pow(smoothstep(16.,64.,dist),0.0));

       //color = vec3(outline); //dbg maybe transition

       vec4 last_frag = (texture2D(backbuffer, uv+nuv*0.005)*vec4(.45,.25,.05,0.25) + texture2D(backbuffer, uv-nuv*0.005)*vec4(.05,.25,.45,0.25))*2.0;
       vec4 last_n0_frag = texture2D(backbuffer, (gl_FragCoord.xy + vec2(1,0)) / resolution.xy);
       vec4 last_n1_frag = texture2D(backbuffer, (gl_FragCoord.xy - vec2(1,0)) / resolution.xy);
       vec4 last_n2_frag = texture2D(backbuffer, (gl_FragCoord.xy + vec2(0,1)) / resolution.xy);
       vec4 last_n3_frag = texture2D(backbuffer, (gl_FragCoord.xy - vec2(0,1)) / resolution.xy);

       vec3 last_color = decode_color(last_frag.rgb);
       vec3 last_n0_color = decode_color(last_n0_frag.rgb) * last_n0_frag.a;
       vec3 last_n1_color = decode_color(last_n1_frag.rgb) * last_n1_frag.a;
       vec3 last_n2_color = decode_color(last_n2_frag.rgb) * last_n2_frag.a;
       vec3 last_n3_color = decode_color(last_n3_frag.rgb) * last_n3_frag.a;

       float last_n_weight_sum = last_n0_frag.a + last_n1_frag.a + last_n2_frag.a + last_n3_frag.a;
       vec3 last_n_color = (last_n0_color + last_n1_color + last_n2_color + last_n3_color) / last_n_weight_sum;
       float last_n_weight = last_n_weight_sum / 4.0;


       color = max(vec3(0), color);
       color = mix(color, last_color, smoothstep(0.,1.,0.55*outline+dist*.001));
       // color += em;
       if (last_n_weight > 1e-2){
               color = mix(color, last_n_color, smoothstep(.0,.9,outline*.55*last_n_weight+dist*0.0025));
       }
       color += vec3((ss_noise(time+9.34)-0.5)*0.005);

       gl_FragColor = vec4( encode_color(color), outline );

}