#define pi 3.14159

#define thc(a,b) tanh(a*cos(b))/tanh(a)
#define ths(a,b) tanh(a*sin(b))/tanh(a)
#define sabs(x) sqrt(x*x+1e-2)
//#define sabs(x, k) sqrt(x*x+k)

#define rot(a) mat2(cos(a), -sin(a), sin(a), cos(a))

float cc(float a, float b) {
    float f = thc(a, b);
    return sign(f) * pow(abs(f), 0.25);
}

float cs(float a, float b) {
    float f = ths(a, b);
    return sign(f) * pow(abs(f), 0.25);
}

vec3 pal(in float t, in vec3 d) {
    return 0.5 + 0.5 * cos(2. * pi * (0.5 * t + d));
}

vec3 pal(in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) {
    return a + b * cos(2. * pi * (c * t + d));
}

float h21(vec2 a) {
    return fract(sin(dot(a.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}

float mlength(vec2 uv) {
    return max(abs(uv.x), abs(uv.y));
}

float mlength(vec3 uv) {
    return max(max(abs(uv.x), abs(uv.y)), abs(uv.z));
}

float sfloor(float a, float b) {
    return floor(b) + 0.5 + 0.5 * tanh(a * (fract(b) - 0.5)) / tanh(0.5 * a);
}

// From iq, k = 0.12 is good
float smin(float a, float b, float k) {
    float h = clamp(0.5 + 0.5 * (b - a) / k, 0., 1.);
    return mix(b, a, h) - k * h * (1. - h);
}

float smax(float a, float b, float k) {
    float h = clamp(0.5 - 0.5 * (b - a) / k, 0., 1.);
    return mix(b, a, h) + k * h * (1. - h); 
}
#define MAX_STEPS 400
#define MAX_DIST 100.
#define SURF_DIST .001

// RayMarching from TheArtOfCode

// From BlackleMori
#define FK(k) floatBitsToInt(k*k/7.)^floatBitsToInt(k)
float hash(float a, float b) {
    int x = FK(a), y = FK(b);
    return float((x*x+y)*(y*y-x)-x)/2.14e9;
}

vec3 erot(vec3 p, vec3 ax, float ro) {
  return mix(dot(ax, p)*ax, p, cos(ro)) + cross(ax,p)*sin(ro);
}

vec3 face(vec3 p) {
     vec3 a = abs(p);
     return step(a.yzx, a.xyz)*step(a.zxy, a.xyz)*sign(p);
}

float sdBox(vec3 p, vec3 s) {
    p = abs(p)-s;
	return length(max(p, 0.))+min(max(p.x, max(p.y, p.z)), 0.);
}

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

vec3 rayOri() {
    vec2 m = iMouse.xy/iResolution.xy;
    float mx = 0.5 - 0.5 * thc(4., 0.2 * iTime);
    vec3 ro = vec3(0, mix(1.5, 0.1, mx), mix(-3., -18., mx));
   // ro.yz *= rot(-m.y*3.14+1.);
    //ro.xz *= rot(-m.x*6.2831);
    return ro;
}

float dist(vec3 p) { 
    p.y += 250. + iTime; // + 2. * cc(1., 0.06 * p.z - iTime);
    p.xz *= rot(3. * atan(p.y,p.z) - 0.1 * iTime);
   // p.y += 0.5 * cos(10. * floor(p.x/3.) + 8. * floor(p.z/3.));
    p = mod(p, 3.) - 1.5;
    float d = length(p) - 0.5;
    return d;
}

vec2 march(vec3 ro, vec3 rd, float z) {	
    float dO=0.;
    float s = sign(z);
    float steps = 0.;
    for(int i=0; i<MAX_STEPS; i++) {
    	vec3 p = ro + rd*dO;
        float dS = dist(p);
        if(abs(dS)<SURF_DIST || dO>MAX_DIST) {
            steps = float(i);
            break;
        }
        if (s != sign(dS)) { z *= 0.5; s = sign(dS); }      
        dO += dS*z; 
    }
    
    return vec2(min(dO, MAX_DIST), steps);
}

vec3 normal(vec3 p) {
	float d = dist(p);
    vec2 e = vec2(.001, 0);
    
    vec3 n = d - vec3(
        dist(p-e.xyy),
        dist(p-e.yxy),
        dist(p-e.yyx));
    
    return normalize(n);
}

vec3 rayDir(vec2 uv, vec3 p, vec3 l, float z) {
    vec3 f = normalize(l-p),
        r = normalize(cross(vec3(0,1,0), f)),
        u = cross(f,r),
        c = f*z,
        i = c + uv.x*r + uv.y*u,
        d = normalize(i);
    return d;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = (fragCoord-.5*iResolution.xy)/iResolution.y;
	
    vec3 ro = rayOri();
    
    vec3 rd = rayDir(uv, ro, vec3(0), 1.45);
    vec3 col = vec3(0);
   
    vec2 m = march(ro, rd, 1.);
    float d = m.x;

    vec3 p = ro + rd * d;      
    vec3 n = normal(p);
    
    rd = mix(rd, 
             rayDir(uv, ro, vec3(0), dist(p)), 
             0.25 + 0.25 * thc(10., 0.5 * length(p) + iTime));
    rd = normalize(rd);
    
    vec3 r = reflect(rd, n);
    vec3 lightDir = normalize(vec3(1,2,3));
    float dif  = dot(n,  lightDir)*.5+.5;
    float fres = pow(1. + dot(rd, n),  4.);
    float v = exp(-0.03 * m.y);
    if (d == MAX_DIST)
        v = 0.;   

    vec3 tx = texture(iChannel0, r).rgb;
  
    // mixing with p.y oversaturates but looks cool
    vec3 bg = mix(vec3(1), 
                  pal(1.25 + 0.05 * cos(0.25 * iTime),
                      0.5 * vec3(0,1,2)/3.), 
                  -abs(p.y));
    
    col = mix(bg * exp(-0.2 * abs(p.y)), 
              0.5 + 0.5 * n,  // was r
              v);
   
    col = mix(col, tx, v * (1.-fres));

    //col = pow(col, vec3(1./2.2));	// gamma correction
    
    fragColor = vec4(col,1.0);
}
