/* Creative Commons Licence Attribution-NonCommercial-ShareAlike 
   phreax 2022
*/

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


float tt, g_mat, flip;
float g_gl = 0.;

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

float fadeInOut(float t1, float t2, float fadeTime) {
    return smoothstep(t1, t1+fadeTime, iTime)-smoothstep(t2-fadeTime, t2, iTime);
}


// Amazing fractal texture from jarble https://www.shadertoy.com/view/csl3zl
vec2 triangle_wave(vec2 a){
    
    vec2 a2 = vec2(1.,0.5),
    
    a1 = a-a2;
    
    return abs(fract((a1)*(a2.x+a2.y))-.5);
}


vec3 fractex(vec2 uv) {
    
    vec3 col = vec3(0.);
    
    uv *= rot2(-tt*.5);
    
    float t1 = 2.;    
    vec2 t2 = vec2(0.);
        
    for(int k = 0; k < 6; k++){
        
        uv = abs(.5*sign(uv.y-uv.x)+uv+t2);
                
        t2 = -triangle_wave(uv-.5*sign(uv.y-uv.x)) ;
        
        uv = t2-triangle_wave(uv.yx);

        float c1 = abs(uv.x-uv.y);
        
        col = col.yzx;
                
        if(uv.y < uv.x) col = vec3(col.yz,c1);
        
    }
    
    col = min(col*2.,vec3(1.));
    return col;
}


// from https://mercury.sexy/hg_sdf/
#define GDFVector3 normalize(vec3(1, 1, 1 ))
#define GDFVector4 normalize(vec3(-1, 1, 1))
#define GDFVector5 normalize(vec3(1, -1, 1))
#define GDFVector6 normalize(vec3(1, 1, -1))

#define GDFVector7 normalize(vec3(0, 1, PHI+1.))
#define GDFVector8 normalize(vec3(0, -1, PHI+1.))
#define GDFVector9 normalize(vec3(PHI+1., 0, 1))
#define GDFVector10 normalize(vec3(-PHI-1., 0, 1))
#define GDFVector11 normalize(vec3(1, PHI+1., 0))
#define GDFVector12 normalize(vec3(-1, PHI+1., 0))

#define GDFVector13 normalize(vec3(0, PHI, 1))
#define GDFVector14 normalize(vec3(0, -PHI, 1))
#define GDFVector15 normalize(vec3(1, 0, PHI))
#define GDFVector16 normalize(vec3(-1, 0, PHI))
#define GDFVector17 normalize(vec3(PHI, 1, 0))
#define GDFVector18 normalize(vec3(-PHI, 1, 0))

#define fGDFBegin float d = 0.;
#define fGDF(v) d = max(d, abs(dot(p, v)));
#define fGDFEnd return d - r;


float icosahedron(vec3 p, float r) {
    fGDFBegin
    fGDF(GDFVector3) fGDF(GDFVector4) fGDF(GDFVector5) fGDF(GDFVector6)
    fGDF(GDFVector7) fGDF(GDFVector8) fGDF(GDFVector9) fGDF(GDFVector10)
    fGDF(GDFVector11) fGDF(GDFVector12)
    fGDFEnd
}


// by Nusan
float curve(float t, float d) {
  t/=d;
  return mix(floor(t), floor(t)+1., pow(smoothstep(0.,1.,fract(t)), 10.));
}

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

float rect( vec2 p, vec2 b, float r ) {
    vec2 d = abs(p) - (b - r);
    return length(max(d, 0.)) + min(max(d.x, d.y), 0.) - r;
}

// fold space for toroid
vec3 transform(vec3 p) {
    float time = tt*flip;

    p.x *= 1.3;
    p.yz *= rot2(PI*.25*flip);

    vec3 a = vec3(
		length(p.xz),
        p.y,
        atan(p.x,p.z)
	);

    a.z = fract((a.z/TAU)*10.+time*.5)-.5;
    a.x -= mix(1.2, 1.9, SIN(tt));
    
    float deformF = fadeInOut(20., 30., 2.);
  
    a.xy *= rot2(abs(p.x)*deformF*.2+time*.5);

    a.xy = abs(a.xy)-.5*SIN(tt);
    
    float rotateF = fadeInOut(10., 53., .0001); 
    float deform2F = fadeInOut(15., 50., 2.);
    a.xy *= rot2(p.x*p.y*.3*deform2F+.33*(iTime-10.)*flip*rotateF);
    
    a.xy = abs(a.xy) -0.3*SIN(.25*tt);
    return a;
}


float map(vec3 p) {   

    vec3 a = transform(p);
    float r = .01;
    float b = mix(.3, .15, SIN(tt));
    
    float fizzleF = fadeInOut(20., 50., 3.); 
    b -= .13*sin(p.x+tt*.33)*fizzleF;

    float w = mix(.2, .6, SIN(.6*tt- PI));
    
    float db = box(a, vec3(b, b, w)) - r;
   
    float dr = rect(a.xy, vec2(b*.8), r);
    
    g_mat = db < dr ? 1. : 0.;
    float d = min(dr, db);;
    return .7*d;
}


vec3 getNormal(vec3 p) {

    vec2 eps = vec2(0.001, 0.0);
    return normalize(vec3(map(p + eps.xyy) - map(p - eps.xyy),
                          map(p + eps.yxy) - map(p - eps.yxy),
                          map(p + eps.yyx) - map(p - eps.yyx)
                         )
                     );
}


vec2 raymarch(vec3 ro, vec3 rd, float steps) {

    float mat = 0.,
          t   = 0.,
          d   = 0.;
    vec3 p = ro;
    for(float i=.0; i<steps; i++) {
    
        d = map(p);
        mat = g_mat;  // save global material
        
        if(d < 0.0001 || t > 50.) break;
        
        t += d;
        p += rd*d;
    }
    
    return vec2(t, mat);
}


void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = (fragCoord-.5*iResolution.xy)/iResolution.y;
    flip = step(gl_FragCoord.x,iResolution.x*.5)*2.-1.;
    //flip = 1.;
    tt = iTime + 17.;
    vec3 ro = vec3(uv*mix(5., 5., SIN(.1*tt)),-4.),
          rd = vec3(0,0,1.),
          lp = vec3(0., 0., -15);

    vec3 col;
       
    float mat = 0.,
          t   = 0.,
          d   = 0.;
 

    vec2 e = vec2(0.0035, -0.0035);
     
    vec3 c1 = vec3(0.365,0.796,0.722);
    vec3 c2 = vec3(0.961,0.667,0.161);
    
    for(float i = 0.; i < 2.; i++) {
        float steps = i > 0. ? 50. : 100.;
        vec2 rm = raymarch(ro, rd, steps);
        mat = rm.y;
        
        vec3 p = ro + rm.x*rd;

        if(rm.x < 50.) {
        
            vec3 rcol = vec3(0);
                    
            vec3 n = 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 l = normalize(lp-p);
            float dif = max(dot(n, l), .0);
            float spe = pow(max(dot(reflect(-rd, n), -l), .0), 40.);
            float sss = smoothstep(0., 1., map(p + l * .4)) / .4;

            vec3 tex = fractex(p.xy*.1);
            vec3 al = mix(c2,  c1 + .4*tex, mat)*.5 + .5*(.5+.5*n);

            rcol += al*mix(1., dif, .8) + .0*spe +  + .4*al*sss;
           
            ro = p + n*0.001; 
            if(mat == 0.) {
                rd = reflect(rd, n);
            } 

			if(i == 0.)
                col = rcol;
            else  
                col *= mix(rcol, vec3(1), 1.0 - exp(-.8*i));  // from https://www.shadertoy.com/view/7dsfDl

        } else {
            
            if(i == 0.) {
               col =  mix(c1, c2-.9, (.9-pow(dot(uv, uv), .3)))*.3+.8; // background
               break;
            } else {
                col = texture(iChannel0, rd).rgb*c2;
            }
        }   
    }


    col *= mix(.2, 1., (1.5-pow(dot(uv, uv), .5)));
    col = pow(col, vec3(.6));
   
    // Output to screen
    fragColor = vec4(col, 1.0);
}
