#define ITERATIONS 6

uniform float iGlobalTime;

varying vec2 vUv;
varying vec3 N;
varying vec3 V;

const float PI = 3.14159265;
vec2 iResolution = vec2(10.0, 10.0);

mat3 rotationMatrix(vec3 axis, float angle)
{
	axis = normalize(axis);
	float s = sin(angle);
	float c = cos(angle);
	float oc = 1.0 - c;
	
	return mat3(oc * axis.x * axis.x + c,           oc * axis.x * axis.y - axis.z * s,  oc * axis.z * axis.x + axis.y * s,  
				oc * axis.x * axis.y + axis.z * s,  oc * axis.y * axis.y + c,           oc * axis.y * axis.z - axis.x * s,  
				oc * axis.z * axis.x - axis.y * s,  oc * axis.y * axis.z + axis.x * s,  oc * axis.z * axis.z + c);
}

vec3 rotateY(vec3 p, float angle)
{
	return p * rotationMatrix(vec3(0.0, 1.0, 0.0), angle);
}

// Floor
vec2 obj_floor(in vec3 p)
{
	return vec2(p.y+10.0,0);
}

vec2 obj_torus(in vec3 p)
{
	p = rotateY(p, iGlobalTime);
	vec2 r = vec2(2.1,0.5);
	vec2 q = vec2(length(p.xy)-r.x,p.z);
	float d = length(q) - r.y;
	return vec2(d,1);
}

vec2 obj_union(in vec2 obj0, in vec2 obj1)
{
	if (obj0.x < obj1.x)
		return obj0;
	else
		return obj1;
}

float smin( float a, float b)
{
	float k = 3.5;
	float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 );
	return mix( b, a, h ) - k*h*(1.0-h);
}

vec2 op_blend( vec3 p, vec2 objA, vec2 objB )
{
	//float d1 = primitiveA(p);
	//float d2 = primitiveB(p);
	return vec2(smin( objA.x, objB.x ), 1);
}

vec2 op_sub(vec2 a, vec2 b)
{
	float d = max(a.x, -b.x);
	return vec2(d,1);
}

vec2 obj_sphere(in vec3 p, in vec3 pos, in float size)
{
	p += pos;
	float d = length(p) - size;
	return vec2(d,1);
}

vec2 obj_round_box(vec3 p)
{
	//float f = 2.0 + p.z / 10.0;
	//p = vec3(p.x, p.y, p.z);
	vec3 size = vec3(5.0,5.0,5.0);
	//p = rotateY(p, (p.y / 10.0) + (sin(iGlobalTime + f) * 2.0 * p.y));
	float d = length(max(abs(p) - size, 0.0)) - 1.0;
	return vec2(d,1);
}

vec2 obj_Quaternion(vec3 p)
{
	vec4 c1 = vec4(-0.2, 0.8, 0, 0); //vec4(0.18, 0.88, 0.24, 0.16);
	vec4 c2 = vec4(-0.213,-0.0410,-0.563,-0.560);
	//vec4 c2 = vec4(-0.445, 0.339, -0.0889, -0.562);

	vec4 c = mix(c1, c2, iGlobalTime);

	vec4 v = vec4(p, 0.0);
	vec4 d = vec4(1.0, 0.0, 0.0, 0.0);

	for(int n = 1; n < ITERATIONS; n++)
	{
		d = 2.0 * vec4(v.x * d.x - dot(v.xzw, d.yzw), v.x * d.yzw + d.x * v.yzw + cross(v.yzw, d.yzw));
		v = vec4(v.x * v.x - dot(v.yzw, v.yzw), vec3(2.0 * v.x * v.yzw)) + c;

		float r = dot(v,v);

		if(r > 10.0) break;
  }

  float r = length(v);

  return vec2(0.5 * r * log(r) / length(d), 1);
}

vec2 distance_to_obj(in vec3 p)
{
	//return obj_union(obj_floor(p), obj_union(obj_torus(p), op_sub(obj_round_box(p), obj_sphere(p))));
	//return obj_union(obj_floor(p), obj_union(obj_sphere(p), obj_torus(p)));
	//return obj_union(obj_floor(p), op_sub(obj_round_box(p), obj_sphere(p)));
	//vec2 sa = obj_sphere(p, vec3(sin(iGlobalTime * 0.9) * 9.0, 0.0, cos(iGlobalTime * 0.6) * 4.0), 3.0 + sin(iGlobalTime * 0.2) * 3.0);
	//vec2 sb = obj_sphere(p, vec3(sin(iGlobalTime * 0.3) * 10.0, cos(iGlobalTime * 0.2) * 7.0, 0.0), 4.0 + sin(iGlobalTime * 0.8) * 1.0);
	//return op_blend(p, sa, sb);
	return vec2(obj_Quaternion(p / 30.0).x * 30.0, 1);
	//return op_blend(p, obj_round_box(p), obj_sphere(p));
}

//Floor Color (checkerboard)
vec3 floor_color(in vec3 p)
{
	if (fract(p.x*0.2)>0.2)
	{
		if (fract(p.z*0.2)>0.2)
			return vec3(0,0.1,0.2);
		else
			return vec3(1,1,1);
	}
	else
	{
		if (fract(p.z*.2)>.2)
			return vec3(1,1,1);
		else
			return vec3(0.3,0,0);
	}
}

// Primitive color
vec3 prim_c(in vec3 p)
{
	return vec3(0.2, 0.2, 0.9) * 1.2;
}

void main() {

	//vec2 vPos = fragCoord.xy/iResolution.xy - 0.5;
	vec2 vPos = vUv - 0.5;

	// Camera up vector.
	vec3 vuv=vec3(0,1,0); 
  
	// Camera lookat.
	vec3 vrp=vec3(0,0,0);

	//float mx=iMouse.x/iResolution.x*PI*2.0;
	//float my=iMouse.y/iResolution.y*PI/2.01;
	//mx = mod(iGlobalTime, 2.0 * PI);
	//vec3 prp=vec3(cos(my)*cos(mx),sin(my),cos(my)*sin(mx))*20.0; 
	//vec3 prp = vec3(0.0, -20.0, 10.0);
	vec3 prp = cameraPosition * vec3(-1.0, 1.0, 1.0);

	// Camera setup.
	vec3 vpn=normalize(vrp-prp);
	vec3 u=normalize(cross(vuv,vpn));
	vec3 v=cross(vpn,u);
	vec3 vcv=(prp+vpn);
	vec3 scrCoord = vcv + vPos.x * u * iResolution.x/iResolution.y + vPos.y * v;
	vec3 scp=normalize(scrCoord-prp);

	// Raymarching.
	const vec3 e=vec3(0.02,0,0);
	const float maxd=400.0; //Max depth
	vec2 d=vec2(0.1,0.0);
	vec3 c,p,N;

	float f=1.0;
	for(int i=0;i<256;i++)
	{
		if ((abs(d.x) < .001) || (f > maxd)) 
			break;

		f+=d.x;
		p=prp+scp*f;
		d = distance_to_obj(p);
	}
  
	if (f < maxd)
	{
		// y is used to manage materials.
		if (d.y==0.0) 
			c=floor_color(p);
		else
			c=prim_c(p);

		vec3 n = vec3(d.x-distance_to_obj(p-e.xyy).x,
				  d.x-distance_to_obj(p-e.yxy).x,
				  d.x-distance_to_obj(p-e.yyx).x);

		N = normalize(n);

		//vec3 L = vec3(sin(iGlobalTime) * 20.0, 10.0, cos(iGlobalTime) * 20.0);

		vec3 L = vec3(7.0, 40.0, -40.0);
		float b=dot(N,normalize(prp-p+L));

		//simple phong lighting, LightPosition = CameraPosition
		gl_FragColor = vec4((b * c + pow(b, 16.0)) * (1.0 - f * .01), 1.0);

	} else {
		gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); //background color
	}
}