#version 330 core

#define T iTime
#define PI 3.141596
#define TAU 6.283185
#define S smoothstep
#define s1(v) (sin(v)*.5+.5)

#define allowRotate

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;
uniform sampler2D iChannel0;

out vec4 FragColor;

const float EPSILON = 1e-3;

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

// iq's sdf function
float sdBoxFrame( vec3 p, vec3 b, float e )
{
       p = abs(p  )-b;
  vec3 q = abs(p+e)-e;
  return min(min(
      length(max(vec3(p.x,q.y,q.z),0.0))+min(max(p.x,max(q.y,q.z)),0.0),
      length(max(vec3(q.x,p.y,q.z),0.0))+min(max(q.x,max(p.y,q.z)),0.0)),
      length(max(vec3(q.x,q.y,p.z),0.0))+min(max(q.x,max(q.y,p.z)),0.0));
}

void mainImage(out vec4 O, in vec2 I){
  vec2 R = iResolution.xy;
  vec2 uv = (I*2.-R)/R.y;
  // mouse not supported in engine - use zero
  vec2 m = vec2(0.0);

  O.rgb = vec3(0.0);
  O.a = 1.0;

  vec3 ro = vec3(0.0,0.0,-10.0);

  vec3 rd = normalize(vec3(uv, 1.0));

  float zMax = 50.0;
  float z = 0.1;

  vec3 col = vec3(0.0);

  mat2 mx = rotate(T);
  mat2 my = rotate(T);

  for(float i=0.0;i<100.0;i++){
    vec3 p = ro + rd * z;

    p.xz *= mx;
    p.yz *= my;

    p = abs(p);
    p-=vec3(1,2,3);
    #ifdef allowRotate
    p.xz*=rotate(6.+T);
    p.xy*=rotate(12.+T);
    p.yz*=rotate(18.+T);
    #endif

    p = clamp(p,0.,TAU);

    vec3 q = cos(p);
    q.yz *= rotate(10.2);

    vec3 q2 = mix(q, p, tanh(sin(T))*0.5+0.5);

    float d = sdBoxFrame(q2, vec3(0.6), 0.02);

    d = abs(d)*0.4 + 0.01;

    col += s1(vec3(3,2,1)+dot(p,q2)+T*2.0)*pow(0.2/d,2.0);
    
    if(d<EPSILON || z>zMax) break;
    z += d;
  }

  col = tanh(col / 2e2);

  O.rgb = col;
  // If a background texture is provided, use its alpha as mask and composite background color
  vec2 uvNorm = I / iResolution.xy;
  vec4 bg = texture(iChannel0, uvNorm);
  float mask = bg.a;
  // blend factor combines mask and effect alpha parameter
  float blendFactor = clamp(mask * uAlpha, 0.0, 1.0);
  O.rgb = mix(bg.rgb, O.rgb, blendFactor);
  // set local alpha to mask; final alpha will be multiplied by uAlpha in main()
  O.a = mask;
}

void main()
{
    vec4 o;
    mainImage(o, gl_FragCoord.xy);
    o.a *= uAlpha;
    FragColor = o;
}
