#version 410 core

uniform float fGlobalTime; // in seconds
uniform vec2 v2Resolution; // viewport resolution (in pixels)
uniform float fFrameTime; // duration of the last frame, in seconds

uniform sampler1D texFFT; // towards 0.0 is bass / lower freq, towards 1.0 is higher / treble freq
uniform sampler1D texFFTSmoothed; // this one has longer falloff and less harsh transients
uniform sampler1D texFFTIntegrated; // this is continually increasing
uniform sampler2D texPreviousFrame; // screenshot of the previous frame
uniform sampler2D texChecker;
uniform sampler2D texNoise;
uniform sampler2D texTex1;
uniform sampler2D texTex2;
uniform sampler2D texTex3;
uniform sampler2D texTex4;

layout(location = 0) out vec4 out_color; // out_color must be written in order to see anything

#define PI acos(-1.)
#define TAU 6.283581
#define ITER 50.

#define rot(a) mat2(cos(a),sin(a),-sin(a),cos(a))
#define crep(p,c,l) p=p-c*clamp(round(p/c),-l,l)

#define dt(sp,off) fract((fGlobalTime+off)*sp)
#define bouncy(sp,off) sqrt(sin(dt(sp,off)*PI))
#define sw(sp,off) floor(sin(dt(sp,off)*TAU)*PI)

struct obj
{
 float d;
 vec3 cs;
 vec3 cl;
};

obj minobj (obj a, obj b)
{
 if (a.d<b.d) return a;
 else return b;
}

float stmin(float a, float b, float k, float n)
{
 float st = k/n;
 float u = b-k;
 return min(min(a,b),0.5*(u+a+abs(mod(u-a+st,2.*st)-st)));
}

void mo (inout vec2 p, vec2 d)
{
 p = abs(p)-d;
 if(p.y>p.x) p = p.yx;
}

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

float sc (vec3 p, float d)
{
 p=abs(p);
 p=max(p,p.yzx);
 return min(p.x,min(p.y,p.z))-d;
}

obj prim1 (vec3 p)
{
 p.x = abs(p.x)-3.;
 float per = 0.9;
 float id = round(p.y/per);
 p.xz *= rot(sin(dt(0.8,id*1.2)*TAU));
 crep(p.y, per,4.);
 mo(p.xz,vec2(0.3));
 p.x += bouncy(2.,0.)*0.8;
 float pd = box(p,vec3(1.5,0.2,0.2));
 return obj(pd,vec3(0.5,0.,0.),vec3(1.,0.5,0.9));
}

obj prim2 (vec3 p)
{
 p.y = abs(p.y)-6.;
 p.z = abs(p.z)-4.;
 mo(p.xz, vec2(1.));
 vec3 pp = p;
 mo(p.yz, vec2(0.5));
 p.y -= 0.5;
 float p2d = max(-sc(p,0.7),box(p,vec3(1.)));
 p = pp;
 p2d = min(p2d, max(box(p,vec3(bouncy(2.,0.))*4.),sc(p,0.2)));
 return obj(p2d, vec3(0.2),vec3(1.));
}

obj prim3 (vec3 p)
{
 p.y -= 1.5;
 p.z = abs(p.z)-10.;
 float per = 0.8;
 vec2 id = round(p.xy/per)-.5;
 float height = 1.*bouncy(2.,sin(length(id*0.05)));
 float p3d = box(p,vec3(2.,2.,0.2));
 crep(p.xy,per,2.);
 p3d = stmin(p3d,box(p+vec3(0.,0.,height*0.9),vec3(0.15,.15,height)),0.2,3.);
 return obj (p3d, vec3(0.1,0.7,0.),vec3(1.,0.9,0.));
}

obj prim4 (vec3 p)
{
 p.y = abs(p.y)-5.;
 mo(p.xz, vec2(1.));
 float scale = 1.5;
 p *= scale;
 float per = 2.*(bouncy(0.5,0.));
 crep(p.xz,per,2.);
 float p4d = max(box(p,vec3(0.9)),sc(p,0.25));
 return obj (p4d/scale, vec3(0.1,0.2,0.4),vec3(0.1,0.8,0.9));
}

float squared (vec3 p,float s)
{
 mo(p.zy,vec2(s));
 return box(p,vec3(0.2,10.,0.2));
}

obj prim5 (vec3 p)
{
 p.x = abs(p.x)-8.;
 float id = round(p.z/7.);
 crep(p.z,7.,2.);
 float scarce = 3.;
 float p5d=1e10;
 for(int i=0;i<4; i++)
 {
   p.x += bouncy(1.,id*0.9)*0.6;
   p5d = min(p5d,squared(p,scarce));
   p.yz *= rot(PI/4.);
   scarce -= 1.;
 }
 return obj(p5d,vec3(0.5,0.2,0.1),vec3(1.,0.9,0.1));
}

obj SDF (vec3 p)
{
 p.yz *= rot(-atan(1./sqrt(2.)));
 p.xz *= rot(PI/4.);

 obj scene = prim1(p);
 scene = minobj(scene,prim2(p));
 scene = minobj(scene,prim3(p));
 scene = minobj(scene,prim4(p));
 scene = minobj(scene, prim5(p));
 return scene;
}


vec3 getnorm (vec3 p)
{
 vec2 eps = vec2(0.001,0.);
 return normalize(SDF(p).d-vec3(SDF(p-eps.xyy).d,SDF(p-eps.yxy).d,SDF(p-eps.yyx).d));
}

void main(void)
{
       vec2 uv = (2.*gl_FragCoord.xy-v2Resolution.xy)/ v2Resolution.y;

 //if (sw(0.1,0.)<0.5) mo(uv,vec2(0.5));

 vec3 ro = vec3(uv*5.,-30.),rd = vec3(0.,0.,1.),
 p = ro,
 col = texture(texFFTSmoothed, length(uv*0.2)).xxx*30.,
 l = normalize(vec3(1.,1.4,-2.));

 obj O; bool hit = false;

 for (float i=0.; i<ITER;i++)
 {
  O = SDF(p);
  if (O.d<0.001)
  {hit = true; break;}
  p += O.d*rd;
 }

 if (hit)
 {
   vec3 n = getnorm(p);
   float light = max(dot(n,l),0.);
   col = mix(O.cs,O.cl, light);
 }



       out_color = vec4(sqrt(col),1.);
}