#define PI 3.141592
#define TAU PI*2.
#define SIN(x) (sin(x)*.5+.5)

#define S(a, b, x) smoothstep(a, b, x)

// interval, 1 if x in [a, b], 0 otherwise
#define I(a, b, x) step(a, x)*step(x, b)


// fadeIn in only defined in interval [a, b]
float fadeInt(float a, float b, float x) {
    return S(a, b, x)*I(a, b, x);
}


float tt;


mat2 rot(float a) { return mat2(cos(a), sin(a), -sin(a), cos(a));}

float smoothrect(float x) {
    return smoothstep(0., .25, mod(x, 1.5))*smoothstep(.25, 0., mod(x, 1.5)-.75);
}

float box(vec3 p, vec3 r) {
	vec3 d = abs(p) - r;
    return length(max(d, 0.0)) + 
        min(max(d.x, max(d.y, d.z)), 0.0);
}



vec3 kaleid(vec3 p) {
    for(int i=0; i<4; i++) {
        p = abs(p) - .1;
        p.xy *= rot(TAU*1./3.);
        p.yz *= rot(TAU*1./16.+(tt-7.5)*.2);
    }
    return p;
}

vec3 fold(vec3 p) {

    float c = cos(PI/5.), s = sqrt(.75 - c*c);
    
    vec3 n = vec3(-.5, -c, s);
    
    p = abs(p);;
    p -= 2.*min(0., dot(p, n))*n;
    
    p.xy = abs(p.xy);
    p -= 2.*min(0., dot(p, n))*n;
    
    p.xy = abs(p.xy);
    p -= 2.*min(0., dot(p, n))*n;
    
    return p;
}

float map(vec3 p) {
    
    vec3 bp = p;
    
    bp.xy *= rot(tt);
    bp.zy *= rot(tt * .5);
    

    float db = box(bp, vec3(1.0));
    float dc = box(fold(bp), vec3(1.0));
    
    p = fold(p);
    
    p = kaleid(p);
    
    float d = box(p, vec3(1.0));


    return mix(mix(db, dc, smoothstep(2., 3., tt)), d, fadeInt(5., 6., tt)+step(6., tt));

}

vec3 getNormal(vec3 p) {
    
    float eps = 0.007;
    vec2 e = vec2(eps, -eps);
    return normalize( e.xyy*map(p+e.xyy) + e.yyx*map(p+e.yyx) +
                            e.yxy*map(p+e.yxy) + e.xxx*map(p+e.xxx));
        
}

vec3 getRayDir(vec2 uv, vec3 p, vec3 l, float z) {

    
    // camera system
    vec3 f = normalize(l - p),  // forward vector
         r = normalize(cross(vec3(0, 1, 0), f)), // right vector
         u = cross(f, r), // up vector
         c = p + f * z, // center of virtual screen
         i = c + uv.x * r + uv.y * u, // intersection with screen
         rd = normalize(i - p);  // ray direction
         
    return rd;
    
}


float rayMarch(vec3 ro, vec3 rd, out vec3 p, float sig) {
    float i, t, d = 0.1;

    p = ro;
    
    for(i=0.; i<100.; i++) {
    
        d = sig*map(p);
        
        if(abs(d) < 0.001 || t > 50.) break;
        	
        p += rd*d;
        t += d;
    }
    
    return d;

}

vec3 refractCol(vec3 p, vec3 rd, vec3 n, float ior) {

        vec3 rdIn = refract(rd, n, 1./ior);
        
        vec3 pEnter = p - n * 0.003;
        
        vec3 pExit;
        float dIn = rayMarch(pEnter, rdIn, pExit, -1.0);
        
        vec3 nExit = -getNormal(pExit);
        vec3 rdOut = refract(rdIn, nExit, ior);
        
        if(dot(rdOut, rdOut) == 0.0) rdOut = reflect(rdIn, nExit);

        return texture(iChannel0, rdOut).rgb;
        
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    tt = iTime;
    tt = mod(tt, 30.0);
    vec2 uv = (fragCoord-.5*iResolution.xy)/iResolution.x;
    vec2 m = iMouse.xy/iResolution.xy;
    
    vec3 ro = vec3(0, 0, -7);
    vec3 lookat = vec3(0, 0, 0);
    vec3 ls = vec3(1, .5, -1.0);
        
    ro.yz *= rot(-m.y*PI+1.);
    ro.xz *= rot(-m.x*TAU);
    
    vec3 rd = getRayDir(uv, ro, lookat, .8);

    vec3 col, p;
    
    float d = rayMarch(ro, rd, p, 1.0);
    


    
    if(d < 0.001) {
        
        vec3 n = getNormal(p);
        vec3 l = normalize(ls);
        float dif = max(dot(n, l), .0);
        float spe = pow(max(dot(reflect(-rd, n), -l), .0), 40.);
        vec3 colR, colG, colB;
        
        float roff = 0.02;
        colR = refractCol(p, rd, n, 1.45);
        colG = refractCol(p, rd, n, 1.45+roff);
        colB = refractCol(p, rd, n, 1.45+2.*roff);
        
        col.r = colR.r;
        col.g = colG.g;
        col.b = colB.b;
   
        col = mix(col, texture(iChannel0, reflect(rd, n)).rgb*(normalize(n+vec3(0.196,1.000,0.141)))+spe+dif, .4*SIN((tt-6.)*.25));
   
    } else {
    	col = texture(iChannel0, rd).rgb;
    }
    

    col = pow(col, vec3(0.4545)); 
    fragColor = vec4(col, 1.);
}
