
#define iTime (iTime + 40.*(iMouse.x/iResolution.x))

#define fov (0.8 + sin(iTime)*0.2)

#define dmin(a, b) (a.x < b.x) ? a : b

vec3 getRd(vec3 ro, vec3 lookAt, vec2 uv) {
  vec3 dir = normalize(lookAt - ro);
    vec3 right = normalize(cross(vec3(0,1,0), dir));
  vec3 up = normalize(cross(dir, right));
  
  return dir*1. + right*uv.x*fov + up*uv.y*fov;
}

#define rot(x) mat2(cos(x), -sin(x), sin(x), cos(x))
#define pi acos(-1.)

#define dmin(a, b) (a.x < b.x) ? a : b // takes 2 vec2, does a minimum of them 

#define pmod(p, x) (mod(p,x) - x*0.5)


float sdBox(vec3 p, vec3 r){
  p = abs(p);
  p-= r;
  return max(p.x, max(p.y, p.z));
}

float sdObjA(vec3 p, float r){  
  p.y *= 0.6;
  p = abs(p);
  p -= r;
  float d = max(p.x, p.y);
  d = max(d, p.z);
  d = max(d, dot(p + 0.2, normalize(vec3(1))));
  return d;
}
vec3 getTunnel(float z) {
  z *= 0.02;
  return vec3(sin(z*0.7)*10. , cos(z*2.) + sin(z*0.8)*10., 0.)*3.;
}


vec2 sdThing(vec3 p) {
  vec2 d = vec2(10e3);
    
  vec3 z = p;
  
  float modDist = 50.;
  
  z -= getTunnel(p.z);
  float id = floor((z.z )/ modDist)- + 0.5*modDist;
  z.z = pmod(z.z, modDist);
  
  int iters = 4;
  for (int i = 0; i<= iters; i++) {
    //z.xy *= rot(-0.25*pi + id*1.4);
    //z.yz *= rot(0.7 + sin(id*0.1)*0.1);
      if( i > 1) {
    	z.xy *= rot(.2 + sin(id*0.8)*0.9);
      }
      if( i > 3) { 
    	z.yz *= rot(0.1 + sin(id*0.6)*0.9 + sin(iTime)*2.5);
      }
    z.y -= 1.4;
    z.x -= 0.4;
      
      
    z.x = abs(z.x);
    z.y = abs(z.y);
    
      if (i == iters ){
          z.x -= 0.9;
      }
      
    
  }
  
  
  p = z;
  //d = min(d,sdBox(p,vec3(0.4,0.7,0.2)) );
  
  d = min(d,sdObjA(p,0.4));
  d = dmin(d, vec2(sdBox(p, vec3(0.3,2.,0.3)), 1.));
  
  d.x *= 0.3;
  return vec2(d);
}




float sdTunnel(vec3 p) {
  float r =10.;
  
  p -= getTunnel(p.z);
  float d = -(length(p.xy) - r);
  
  
  float reprng = 10.;
  
  
  //reprng *= 1. + sin(p.z*0.5)*0.1;
  
  for (int i = 0; i < 5;i++) {
      p.xy *= rot(0.5 + p.z);
  
      p.y -= 0.4;
      p = abs(p);
  }
  float id = -p.z/ reprng;
  
  //p.z -= 7.*(sin(id*15.12));
  p.z -= 10.*(sin(id*0.5));
    
  d = max(d, abs(mod(-p.z, reprng)) - 0.4 + 0.2);
  //d = max(d,  abs(mod(-p.y, reprng)) + 0.01 );
  //d = max(d, abs(mod(p.x, reprng)) - 0.7);
  
  d *= 0.6;
  return d;
}

#define spectra(x,t ) (1. + sin(vec3(0.9,0.1,0.3)*t + x + iTime))

vec2 map(vec3 p){ // gets distance to everything
  vec2 d = vec2(10e3); // d.x = distance, d.y = id

    
  d = dmin(d, sdThing(p));
  d = dmin(d, vec2(sdTunnel(p),3));
  

  //d.x = length(p) - 1.;
  
  return d;
}

vec3 getNormal(vec3 p) {
    vec2 t = vec2(0.001, 0.);
  return normalize(map(p).x - vec3(
    map(p - t.xyy).x,
    map(p - t.yxy).x,
    map(p - t.yyx).x
  ));
}

#define zoom 30.



float softshadow( in vec3 ro, in vec3 rd, float mint, float maxt, float w )
{
    float s = 1.0;
    float t = 0.1;
    for( float i=0.; i<100.; i++ )
    {
        float h = map(ro + rd*t).x;
        s = min( s, 0.5+0.5*h/(w*t) );
        if( s<0.0 ) break;
        t += h;
    }
    s = max(s,0.0);
    return s*s*(3.0-2.0*s); // smoothstep
}

vec3 glow = vec3(0);
#define rotSpeed 1.
vec4 render(vec2 uv) {
  vec3 col = vec3 (0);
  
  //vec3 ro = vec3(sin(iTime*rotSpeed)*zoom,0,cos(iTime*rotSpeed) * zoom);
  
  

  vec3 ro = vec3(0,0.,0. + iTime*60.);
  //ro.z = 160;
  vec3 currTunn = getTunnel(ro.z);
  vec3 nextTunn = getTunnel(ro.z + 5. + sin(iTime)*3.);
  vec3 lookAt = ro + vec3(0,0,4)  + vec3(nextTunn.xy,0.);
  ro += currTunn;
  vec3 rd = getRd(ro, lookAt, uv);
  
  rd.xy *= 1. + pow(length(uv.xy)*1.2, 6.4 + sin(iTime)*5.);
  rd = normalize(rd);
  vec3 p = ro; 
  float t = 0.;
  
  for (int i = 0; i < 250; i++) {
      vec2 d = map(p);
      
      glow += spectra(d.x + p.z*0.05, 5.);
      
      if (d.x < 0.001) {

        
        vec3 dirLight = normalize(vec3(4.));
        dirLight += vec3(sin(iTime)*10., 0.,0.);

        vec3 n = getNormal(p);


        
        float diff = max(dot(dirLight, n), 0.);
        float fres = pow(max(dot(-rd, n), 0.),5.);
        float spec = pow(max(dot(-rd, n), 0.),5.);
 
        col += fres*diff * vec3(0.1);

        //col += fres;
          
          break;
      }
      
      if (d.x > 100.) {
        col = vec3(0.01);
        break;
      }
      
      
      
      t += d.x;
      p = ro + rd * t;
  }
  col += glow*0.03;
  //col *= glow*0.04;
  col = pow(col, vec3(1.  - sin(iTime*0.2)*0.2,1.2 + sin(iTime)*0.2,1.));
  col *= 0.2;
  col = pow(col, vec3(0.44));
  
  return vec4(col, 0);
}

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

    fragColor = render(uv);

    //fragColor = vec4(col,1.0);
}
