//Set cheap to 1 to use Torus for rings, which will run much faster.
#define CHEAP 1
#define PI 3.14159265359

// polynomial smooth min from IQ
float sminCubic( float a, float b, float k )
{
    float h = max( k-abs(a-b), 0.0 )/k;
    return min( a, b ) - h*h*h*k*(1.0/6.0);
}

//fog from IQ
vec3 applyFog( in vec3  rgb,
               in float d )
{
    float fogAmount = 1.0 - exp( -d*0.02 );
    vec3  fogColor  = vec3(0.5,0.6,0.7);
    return mix( rgb, fogColor, fogAmount );
}

//Modfied torus (to make size not uniform)
float sdTorus( vec3 p, vec2 t )
{
    float a = atan(p.y/p.x);
    vec2 q = vec2((length(p.xy))-t.x,p.z);
    return length(q)-(t.y - 0.25*sin(a*10.));
}

vec2 rot( vec2 p, float a )
{
    float co = cos(a);
    float si = sin(a);
    return mat2(co,-si,si,co) * p;
}

vec4 map(vec3 pos, out int id)
{
    vec3 realPos = pos;
    vec3 col = vec3(0.0);
    float rad = 1.0;
    float mind = 100.0;
    
    mind = -abs(pos.x) + 10.0*textureLod(iChannel0, vec2(iTime/500.0,0) + realPos.yz/100.0, 0.).x - -25.0;
    if(abs(mind) < 0.01)
    {
        col = vec3(0.1,0,1);
        col *= 0.5;
        id = 0;
    }
    
    float x = cos(floor((pos.z) / 20.0))*14.0;
    float y = sin(floor((pos.z) / 20.0))*10.0;
    float zang = cos(floor((pos.z) / 20.0))*10.0;
    pos.x -= x;
    pos.y -= y;
    pos.z = -10.0 + mod(pos.z, 20.0);

    float realZ = realPos.z - 10.0;
    realPos.z = mod(realPos.z, 1.0);
    realPos.x -= cos(realZ / 20.0)*14.0;
    realPos.y -= sin(realZ / 20.0)*10.0;
    realPos.xy = abs(realPos.xy);
    vec3 v = realPos - vec3(5.0, 3.0, 0.5);
    float dPipes = sminCubic(mind, length(v) - 0.5, 1.0); //distance to sphere pipes
    realPos.z /= 10.0;
    v = realPos - vec3(2, 4.0, 0.05);
    dPipes = sminCubic(dPipes, length(v) - 0.1, 1.0); //thin pipes
    if(dPipes < mind)
    {
        mind = dPipes;
        col = vec3(0,1,3)* (1.5 + 0.5*sin(realZ/5.0 + 5.0*iTime));
        id = 1;
    }

    pos.yz = rot(pos.yz, -(PI/8.0) * sign(zang));
    pos.xz = rot(pos.xz, (PI/8.0) * sign(y));
    pos.xy = rot(pos.xy, PI/10.0 * iTime);
    
#if CHEAP>0
    mind = sminCubic(mind, sdTorus(pos, vec2(5., 1.0)), 3.5);
#else
    for(int i = 0; i < 10; ++i)
    {
            
        vec3 p = vec3(5.0, 0, 0.0);
        vec3 v = pos - p;
        float dRing = sminCubic(mind, length(v) - rad, 3.5); //distance to rings
        if(dRing < mind)
        {
          mind = dRing;
          col = vec3(0,1,3)* (1.5 + 0.5*sin(realZ/5.0 + 5.0*iTime));
          id = 2;
        }
            
        pos.xy = rot(pos.xy, PI/5.0);
    }
#endif

    return vec4(mind, col);
}

vec3 moveCam(out vec3 fwd)
{
    float z = 10.0+(-iTime) * 10.0;
    float x = cos((z - 10.0) / 20.0)*14.0;
    float y = sin((z - 10.0) / 20.0)*10.0;
    
    vec3 pos = vec3(x, y, z);
    fwd = normalize(vec3(cos((z + 10.0) / 20.0)*14.0, sin((z + 10.0) / 20.0)*10.0, z + 20.0) - pos);
   
    return pos;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = (fragCoord*2.0 - iResolution.xy) / iResolution.y;
    vec3 fwd = vec3(0);
    vec3 camPos = moveCam(fwd);
    vec3 r = normalize(cross(fwd, vec3(0,1,0)));
    vec3 u = normalize(cross(r, fwd));
    vec3 dir = normalize((r * uv.x) + (u * uv.y) + vec3(0,0,-1));
    vec3 col = vec3(0, 0, 0.2);
    vec3 pos = camPos;
    vec3 n   = vec3(0);
    bool hit = false;
    float rad = 1.0;
    int id = -1;
    
    
    for(int i = 0; i < 128; ++i)
    {
        vec4 dc = map(pos, id);
        float d = dc.x;
        
        if(abs(d) <= 0.01)
        {
            col = dc.yzw;
            int dummy = 0;
            float h = 0.01;
            vec2 hv = vec2(h, 0);
            n = normalize(vec3(map(pos + hv.xyy, dummy).x - d, map(pos + hv.yxy, dummy).x - d, map(pos + hv.yyx, dummy).x - d));
            hit = true;
            break;
        }
          
        pos += dir * d * 0.9;
    }

    // Output to screen
    col = hit && id != 0 ? col * texture(iChannel1, reflect(n, dir)).rgb : n*col;
    col = applyFog(col, length(pos- camPos));
    fragColor = vec4(col, 1.0);
}
