// Code by Flopine

// Thanks to wsmind, leon, XT95, lsdlive, lamogui,
// Coyhot, Alkama,YX, NuSan and slerpy for teaching me

// Thanks LJ for giving me the spark :3

// Thanks to the Cookie Collective, which build a cozy and safe environment for me
// and other to sprout :)  https://twitter.com/CookieDemoparty


#define PI acos(-1.)
#define TAU 6.283185

#define mo(p,d)p=abs(p)-d;if(p.y>p.x)p=p.yx
#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 hash11(x) fract(sin(x)*146.4)

#define dt(speed) fract(iTime*speed)

// taken from YX here : https://www.shadertoy.com/view/tdlXW4
// rough shadertoy approximation of the bonzomatic noise texture
vec4 texNoise(vec2 uv)
{
    float f = 0.;
    f += texture(iChannel0, uv*.125).r*.5;
    f += texture(iChannel0, uv*.25).r*.25;
    f += texture(iChannel0, uv*.5).r*.125;
    f += texture(iChannel0, uv*1.).r*.125;
    f=pow(f,1.2);
    return vec4(f*.45+.05);
}

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 moda (inout vec2 p, float rep)
{
  float per = (2.*PI)/rep;
  float a = mod(atan(p.y,p.x),per)-per*0.5;
  p = vec2(cos(a),sin(a))*length(p);
}

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 cyl(vec3 p, float r, float h)
{return max(length(p.xy)-r,abs(p.z)-h);}

float torus (vec3 p, vec2 t)
{
  vec2 q = vec2(length(p.xz),p.y)-t.x;
  return length(q)-t.y;
}

float buildings (vec3 p)
{
  vec3 pp = p;

  mo(p.xz, vec2(10.));
  float g = cyl(p.xzy,0.5,50.);
  p.x -= 5.;
  float d = min(g,box(p,vec3(5.,50.,6.5)));

  p = pp;
  p.z -= 3.5;
  p.x = abs(p.x)-15.;
  crep(p.x, 1.,4.);
  d = max(d,-cyl(p.xzy,0.3,45.));

  return d;
}

float staircase (vec3 p)
{
  p.xz *= rot(PI/4.);
  float width = 3.;
  vec3 pp = p;
  float d = cyl(p.xzy, 0.1, 50.);

  float per = 10.;
  p.xz *= rot(PI/5.);
  crep(p.y,per,3.);
  moda(p.xz,5.);
  p.x -= width;
  d = min(d,box(p,vec3(0.25,0.25,5.)));

  p =pp;
  moda(p.xz,5.);
  p.x -= width*0.95;
  d = min(d,cyl(p.xzy,0.1,50.));

  p = pp;
  float sper = 1.;
  float sid = round(p.y/sper);
  crep(p.y,sper, 50.);

  p.xz *= rot(sid*0.3);
  p.x -= width*0.5;
  d = min(d,box(p,vec3(width*0.6,0.15,1.)));

  return d;
}

float staircases (vec3 p)
{
  p.xz *= rot(PI/4.);
  moda(p.xz, 4.);
  p.x -= 20.;
  return staircase(p);
}

float balcony (vec3 p)
{
  mo(p.xz, vec2(10.));
  float per = 8.;
  crep(p.y,per,6.);
  p.xz *= rot(PI/4.);
  float d = box(p,vec3(4.,1.5,4.));

  crep(p.y,per,6.);
  p.x += 2.;
  d = max(d,-box(p,vec3(4.,0.2,4.)));

  return d;
}

float g1 = 0.; float gid;
float gems (vec3 p)
{
  p.y -= 4.;
  mo(p.xz,vec2(10.));
  p.z += 4.;
  gid = round(p.y/8.);
  crep(p.y, 8.,6.);
  float d = dot(p,normalize(sign(p)))-0.4;

  g1 += 0.01/(0.01+d*d);

  return d;
}

float SDF (vec3 p)
{
  float scene = stmin(balcony(p),buildings(p),0.4,2.);
  scene = min(scene,staircases(p));
  scene = min(scene,gems(p));

  return scene;
}

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

vec3 getcam (vec3 ro, vec3 ta, vec2 uv)
{
  vec3 f = normalize(ta-ro);
  vec3 l = normalize(cross(vec3(0.,1.,0.),f));
  vec3 u = normalize(cross(f,l));
  return normalize(f+l*uv.x+u*uv.y);
}


void mainImage (out vec4 fragColor, in vec2 fragCoord)
{
  vec2 uv = (2.*fragCoord.xy - iResolution.xy)/iResolution.y;

  vec3 ro=vec3(1.2,-6.5,-7.), rd=getcam(ro,vec3(0.),uv), p=ro, col=vec3(0.), l=normalize(vec3(1.,2.,-3.));

  bool hit=false;

  for(float i=0.;i<64.;i++)
  {
    float d = SDF(p);
    if (d<0.001)
    {
      hit = true;
      break;
    }
    p += d*rd*0.8;
  }
  float t = length(ro-p);

  if (hit)
  {
    vec3 n = getnorm(p);
    col = vec3(dot(n,l)*0.5+0.25);
  }

  col = mix(col,
          vec3(0.1,0.4,0.9),
          1.-exp(-0.0003*t*t)+texNoise(uv*0.45).x*length((p.y+3.)*0.2));
  col += g1*hash11(gid+dt(0.03))*0.35;
  col = pow(col,vec3(2.));

  fragColor = vec4(sqrt(clamp(col,0.,1.)),1.);
}
