#version 450 core

// inputs
in vec2 texCoord;

// outputs
//layout (location = 0) out fragColor;

// uniforms
uniform float       g_time;
uniform vec2        g_resolution;
uniform float       g_morph;
uniform float       g_dissolve;
uniform float       g_patternscale;
uniform float       g_rotatorStuff;

float t = g_time;
vec2 rotate(vec2 r, float a) { return r*mat2(cos(a), -sin(a), sin(a), cos(a)); }

#define MARCHING_STEPS 30
#define EPS   0.0001

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

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

float sdf1(vec3 p) {
    vec3  n = normalize(p);
    float c = sb(p, vec3(2.0), 2.5);
    c += -0.6*sin(t*0.72)*sin(11.0*n.x+0.9*t)*cos(12.0*n.y+0.8*t)*cos(10.0*n.z+1.1*t)-0.7
    + g_dissolve*0.07;
    return c;
}

float map(vec3 p) {
	//return sdRoundBox(p, vec3(0.4), 0.2);
	vec3 pr = vec3(rotate(g_morph > 0.0 ? abs(p.xy*g_morph) : p.xy, t), p.z);
	pr = vec3(pr.x, rotate(pr.yz, t));
	if(g_morph > 0.0) {
		pr = abs(pr);
	} 
	
	
	return sdf1(pr);
}

///*
float raymarch(vec3 ro, vec3 rd, float min, float max) {
	float d = min;
	
    for (int i = 0; i < MARCHING_STEPS; i++) {
    	float dist = map(ro + d*rd);
        if ((dist < EPS) || (d >= max)) return d;
        d += dist;
    }
    return max;
}

mat3 lookAt(vec3 eye, vec3 center, vec3 up) {
    vec3 f = normalize(center - eye);
    vec3 s = normalize(cross(f, up));
    vec3 u = cross(s, f);
    return mat3(s,u,-f);
}  

vec3 normal(vec3 p) {
	float e = 0.001;
	vec3 diff = vec3(map(vec3(p.x+e, p.y, p.z)),
	                 map(vec3(p.x, p.y+e, p.z)),
	                 map(vec3(p.x, p.y, p.z+e)));
	return normalize(vec3(map(p)) - diff);
}

vec3 rd(vec2 p, vec2 r) {
    vec2 d = p - r/2.0; // center screen
    float z = r.y / 3.7;
    return normalize(vec3(d, -z));
}


// lame phong
float ph(float ka, float kd, float ks, vec3 p, vec3 e, vec3 l, float i, float a) {
    vec3 N = normal(p);
    vec3 L = normalize(l - p);
    
    vec3 R = reflect(-L, N);
    vec3 V = normalize(e - p);
    
    float dNL = max(dot(N, L), 0.0);
    float dRV = max(dot(R, V), 0.0);
    
    return (ka * i) + (kd * dNL) + (ks * pow(dRV, a));
}

void main(void)
{

	vec2 uv = texCoord * 2 - 1;
	uv.y /= (g_resolution.x / g_resolution.y);
	
	//uv.x += 0.4*sin(g_time);
	//uv.y += 0.4*cos(g_time);
	
	// backdrop
	vec2 uvrot = rotate(abs(uv*g_patternscale), g_time+1.2*sin(g_time*1.3));
	uvrot = (1.3+1.2*sin(g_time))*abs(uvrot);
	if (g_rotatorStuff > 0.0) {
		uvrot = rotate(abs(uvrot + vec2(g_rotatorStuff, 0.0)), t*0.3);
	}
		
	vec3 backdrop[4] = vec3[](
					 vec3(0.870, 0.478, 0.133),
					 vec3(0.976, 0.462, 0.462),
					 vec3(1, 1, 1),
					 vec3(0.117, 0.262, 0.298));
	
	
    float bdfloat = 0.5*mod(pow(uvrot.x + uvrot.y + g_time, 1.1), 1.0) - 0.5;
    //float mu = 1;
    //float bdmod = 2*(sign(bdfloat)*log(1+mu*abs(bdfloat))/log(1+mu)) + 1;
	int bdcolor = clamp(int((2*bdfloat+1) * 4.), 0, 3);
	//int bdcolor = clamp(int(bdmod * 4.), 0, 3);
	
	// calculate camera rotation
    
    // calculate camera origin and direction
    vec3 o  = vec3(0.0, 0.0, 8.0);
    vec3 e  = vec3(0.0, 0.0, 0.0);
    vec3 l  = 3.0*vec3(sin(t), cos(t), cos(t));

	vec3 wd = rd(gl_FragCoord.xy, g_resolution);
	
	float color = -1.0;
	// layer 1 - outlines only
	float d = raymarch(o-vec3(0,0,0.5), wd, 0.0, 256.0);
	color = (d.x < 255.999 ? 1.0 : color);
	
	// layer 2 - actual object
	d = raymarch(o, wd, 0.0, 256.0);
	color = (d.x < 255.999 ? ph(0.1, 0.2, 0.7, o+wd*d, e, l, 0.0, 32) : color);
	
	// post result
	vec3 outColor = (color == -1.0 ? backdrop[bdcolor] : color*vec3(1.0));
	gl_FragColor = vec4(outColor, 1.0);
	return;
}





