uniform float iGlobalTime;
uniform vec3 iResolution;
uniform samplerCube iChannel0;

uniform float u_camdist;
uniform float u_bubbles;
uniform float u_lookup;

struct ray_t 
{
	vec3 o;
	vec3 d;
};

float ray_to_sphere(in ray_t ray, in vec4 spr_o)
{
	vec3 d1 = ray.o - spr_o.xyz;
	float b = 2.0 * dot(ray.d, d1);
	float c = dot(d1,d1) - spr_o.w*spr_o.w;

	float delta = b*b - 4.0*c;

	if (delta >= 0.0)
	{
		float t = (-b-sqrt(delta))/ (2.0);
		
		if (t > 0.0)
		{
			return t;
		}
	}

	return 1000000.0;
}

float ray_to_plane(in ray_t ray, in vec4 planevec)
{
	float deter = dot(planevec.xyz,ray.d);
	if (abs(deter) < 0.00001)
		return 1000000.0;

	float d = (planevec.w - dot(planevec.xyz,ray.o)) / deter;
//	if (d > 0.01)
	  return d;

	   return 1000000.0;
}

vec4 obj_pos[13];
vec3 lp;
vec3 lp2;
float lp2_amount;
vec3 campos_fudge;
vec3 camdir_fudge;
float boom;

void update_positions()
{
//	lp = vec3(0.0+sin(iGlobalTime * 3.0) * 2.0, 2.0, 0.0);
	lp = vec3(0.0+sin(iGlobalTime * 3.0) * 2.0, 5.0, cos(iGlobalTime * 1.0) * 20.0);

	float bbang = iGlobalTime * 2.0 * (134.0/120.0);
	obj_pos[0] = vec4(-2.0, 0.0 + sin(iGlobalTime * 2.3), 0.0, 1.2 + sin(iGlobalTime * 1.5 + gl_FragCoord.y * 0.01) * 0.1);
//	obj_pos[1] = vec4(0, sin(iGlobalTime*3.0), 0.0, 1.2);
//	obj_pos[2] = vec4(2, sin(iGlobalTime*2.3), 0.0, 1.2);
	obj_pos[1] = vec4(0.0, -1.3, 0.0, 1.2);
	obj_pos[2] = vec4(3.0, -1.5 + sin(mod(bbang, 3.14159)) * 3.0, 0.0, 1.2);
	lp2 = vec3(3.0, -1.5 + sin(mod(bbang, 3.14159)) * 3.0, 0.0);
	obj_pos[3] = vec4(normalize(vec3( 0.0, -1.0,  0.0)),  2.5);
	obj_pos[4] = vec4(normalize(vec3( 0.0,  -0.0,  1.0)), -7.0);
	obj_pos[5] = vec4(normalize(vec3( 0.0,  -0.0, -1.0)), -7.0);
	obj_pos[6] = vec4(normalize(vec3( 1.0,  0.5,  0.0)), -7.0);
	obj_pos[7] = vec4(normalize(vec3(-1.0,  -0.5,  0.0)), -7.0);

	
	obj_pos[8] = vec4(normalize(vec3( 0.0, -1.0,  0.0)),  7.5);

	float tfake = u_bubbles;
	obj_pos[9] = vec4(-0.5, -1.3, -2.4, 1.2);
	obj_pos[10] = vec4(0.3, 0.4 + sin(iGlobalTime * 2.111) * 0.35, 5.0, 2.5);
	obj_pos[11] = vec4(12.0, -9.0 + tfake, -10.0, 2.5);
	obj_pos[11].xyz += vec3(cos(iGlobalTime * 2.6), sin(iGlobalTime * 1.2543), sin(iGlobalTime * 2.8)) * 0.5;
	obj_pos[12] = vec4(14.0, -13.0 + tfake * 1.7, -2.0, 1.5);
	obj_pos[12].xyz += vec3(cos(iGlobalTime * 2.999), sin(iGlobalTime * 1.452), sin(iGlobalTime * 2.6223)) * 0.5;


	float fudgemod = 1.0 / (1.0 + u_camdist * 0.5);
	float fudgestren = pow((1.0 - mod(bbang, 3.14159)/3.14159),3.0) * 0.04 * fudgemod;
	float fudgestren2 = pow((1.0 - mod(bbang, 3.14159)/3.14159),3.0) * 0.02 * fudgemod;
	//fudgestren = 0.0;
	float t = iGlobalTime;

	boom = pow((1.0 - mod(bbang, 3.14159)/3.14159),3.0);

	lp2_amount = pow((1.0 - mod(bbang, 3.14159)/3.14159),1.0);
	
	campos_fudge = sin(vec3(t)*vec3(39.1, 38.4, 39.3)) * fudgestren;
	camdir_fudge = sin(vec3(t)*vec3(79.1, 78.4, 69.3)) * fudgestren2;
}
/*
float distance(vec3 pos )
{
	float d, dc;
	d = 1000000.0;


	dc = ray_to_sphere(ray, obj_pos[0]);
	d = min(d, dc);
	dc = ray_to_sphere(ray, obj_pos[1]);
	d = min(d, dc);
	dc = ray_to_sphere(ray, obj_pos[2]);
	d = min(d, dc);
	dc = ray_to_sphere(ray, obj_pos[9]);
	d = min(d, dc);
	dc = ray_to_sphere(ray, obj_pos[10]);
	d = min(d, dc);

	float dcmin = -1000000.0;
	float dcmax =  1000000.0;

	float dc1,dc2,dc3,dc4,dc5,dc6;

	dc1 = ray_to_plane(ray, obj_pos[4]);
	dc2 = ray_to_plane(ray, obj_pos[5]);
	dcmin = max(dcmin, min(dc1, dc2));
	dcmax = min(dcmax, max(dc1, dc2));

	dc3 = ray_to_plane(ray, obj_pos[6]);
	dc4 = ray_to_plane(ray, obj_pos[7]);
	dcmin = max(dcmin, min(dc3, dc4));
	dcmax = min(dcmax, max(dc3, dc4));

	dc5 = ray_to_plane(ray, obj_pos[3]);
	dc6 = 1000000.0 * -sign(ray.d.y);
	dcmin = max(dcmin, min(dc5, dc6));
	dcmax = min(dcmax, max(dc5, dc6));
	
	if (dcmin < dcmax)
		dc = dcmin;
	else
		dc = 1000000.0;

	d = min(d, dc);

	dc = ray_to_plane(ray, obj_pos[8]);
	d = min(d, dc);

	return d;
}
*/


float trage_shadow(inout ray_t ray)
{
	vec4 lt;
	float dc;

	float d;
	float mat = 0.0;

	d = 1000000.0;
	
	dc = ray_to_sphere(ray, obj_pos[0]);
	if (dc < 100000.0 && dc < d) { d = dc;}

	dc = ray_to_sphere(ray, obj_pos[1]);
	if (dc < 100000.0 && dc < d) { d = dc; }

	dc = ray_to_sphere(ray, obj_pos[2]);
	if (dc < 100000.0 && dc < d) { d = dc; }

	dc = ray_to_sphere(ray, obj_pos[9]);
	if (dc < 100000.0 && dc < d) { d = dc; }

	dc = ray_to_sphere(ray, obj_pos[10]);
	if (dc < 100000.0 && dc < d) { d = dc; }


	float dcmin = -1000000.0;
	float dcmax =  1000000.0;

	float dc1,dc2,dc3,dc4,dc5,dc6;

	dc1 = ray_to_plane(ray, obj_pos[4]);
	dc2 = ray_to_plane(ray, obj_pos[5]);
	dcmin = max(dcmin, min(dc1, dc2));
	dcmax = min(dcmax, max(dc1, dc2));

	dc3 = ray_to_plane(ray, obj_pos[6]);
	dc4 = ray_to_plane(ray, obj_pos[7]);
	dcmin = max(dcmin, min(dc3, dc4));
	dcmax = min(dcmax, max(dc3, dc4));

	dc5 = ray_to_plane(ray, obj_pos[3]);
	dc6 = 1000000.0 * -sign(ray.d.y);
	dcmin = max(dcmin, min(dc5, dc6));
	dcmax = min(dcmax, max(dc5, dc6));
	
	if (dcmin < dcmax)
		dc = dcmin;
	else
		dc = 1000000.0;

	if (dc > 0.0 && dc < 100000.0 && dc < d)
	{
		d = dc; 
	}

	dc = ray_to_plane(ray, obj_pos[8]);
	if (dc > 0.0 && dc < 100000.0 && dc < d) { d = dc; }

	return d;
}

float trage_shadow2(inout ray_t ray)
{
	vec4 lt;
	float dc;

	float d;
	float mat = 0.0;

	d = 1000000.0;
	
	dc = ray_to_sphere(ray, obj_pos[0]);
	if (dc < 100000.0 && dc < d) { d = dc;}

	dc = ray_to_sphere(ray, obj_pos[1]);
	if (dc < 100000.0 && dc < d) { d = dc; }

	//dc = ray_to_sphere(ray, obj_pos[2]);
	//if (dc < 100000.0 && dc < d) { d = dc; }

	dc = ray_to_sphere(ray, obj_pos[9]);
	if (dc < 100000.0 && dc < d) { d = dc; }

	dc = ray_to_sphere(ray, obj_pos[10]);
	if (dc < 100000.0 && dc < d) { d = dc; }


	float dcmin = -1000000.0;
	float dcmax =  1000000.0;

	float dc1,dc2,dc3,dc4,dc5,dc6;

	dc1 = ray_to_plane(ray, obj_pos[4]);
	dc2 = ray_to_plane(ray, obj_pos[5]);
	dcmin = max(dcmin, min(dc1, dc2));
	dcmax = min(dcmax, max(dc1, dc2));

	dc3 = ray_to_plane(ray, obj_pos[6]);
	dc4 = ray_to_plane(ray, obj_pos[7]);
	dcmin = max(dcmin, min(dc3, dc4));
	dcmax = min(dcmax, max(dc3, dc4));

	dc5 = ray_to_plane(ray, obj_pos[3]);
	dc6 = 1000000.0 * -sign(ray.d.y);
	dcmin = max(dcmin, min(dc5, dc6));
	dcmax = min(dcmax, max(dc5, dc6));
	
	if (dcmin < dcmax)
		dc = dcmin;
	else
		dc = 1000000.0;

	if (dc > 0.0 && dc < 100000.0 && dc < d)
	{
		d = dc; 
	}

	dc = ray_to_plane(ray, obj_pos[8]);
	if (dc > 0.0 && dc < 100000.0 && dc < d) { d = dc; }

	return d;
}


float trage(inout ray_t ray, out vec3 normal, out vec4 color)
{
	vec4 lt;
	float dc;

	float d;
	float mat = 0.0;

	d = 1000000.0;
	
	dc = ray_to_sphere(ray, obj_pos[0]);
	if (dc < 100000.0 && dc < d) { d = dc; mat = 1.0; }

	dc = ray_to_sphere(ray, obj_pos[1]);
	if (dc < 100000.0 && dc < d) { d = dc; mat = 2.0; }

	dc = ray_to_sphere(ray, obj_pos[2]);
	if (dc < 100000.0 && dc < d) { d = dc; mat = 3.0; }

	dc = ray_to_sphere(ray, obj_pos[9]);
	if (dc < 100000.0 && dc < d) { d = dc; mat = 10.0; }

	dc = ray_to_sphere(ray, obj_pos[10]);
	if (dc < 100000.0 && dc < d) { d = dc; mat = 11.0; }

	dc = ray_to_sphere(ray, obj_pos[11]);
	if (dc < 100000.0 && dc < d) { d = dc; mat = 12.0; }

	dc = ray_to_sphere(ray, obj_pos[12]);
	if (dc < 100000.0 && dc < d) { d = dc; mat = 13.0; }

	float dcmin = -1000000.0;
	float dcmax =  1000000.0;

	float dc1,dc2,dc3,dc4,dc5,dc6;

	dc1 = ray_to_plane(ray, obj_pos[4]);
	dc2 = ray_to_plane(ray, obj_pos[5]);
	dcmin = max(dcmin, min(dc1, dc2));
	dcmax = min(dcmax, max(dc1, dc2));

	dc3 = ray_to_plane(ray, obj_pos[6]);
	dc4 = ray_to_plane(ray, obj_pos[7]);
	dcmin = max(dcmin, min(dc3, dc4));
	dcmax = min(dcmax, max(dc3, dc4));

	dc5 = ray_to_plane(ray, obj_pos[3]);
	dc6 = 1000000.0 * -sign(ray.d.y);
	dcmin = max(dcmin, min(dc5, dc6));
	dcmax = min(dcmax, max(dc5, dc6));
	
	if (dcmin < dcmax)
		dc = dcmin;
	else
		dc = 1000000.0;

	if (dc > 0.0 && dc < 100000.0 && dc < d)
	{
		d = dc; 
		if (d == dc1)
			mat = 5.0;
		else if (d == dc2)
			mat = 6.0;
		else if (d == dc3)
			mat = 7.0;
		else if (d == dc4)
			mat = 8.0;
		else if (d == dc5)
			mat = 4.0;
	}

	dc = ray_to_plane(ray, obj_pos[8]);
	if (dc > 0.0 && dc < 100000.0 && dc < d) { d = dc; mat = 9.0; }
	
	
	
	color = textureCube(iChannel0, ray.d);
	
	if (mat > 0.5)
	{
		vec3 hp = ray.o + ray.d * d;
		if (mat < 1.5)
		{
			normal = normalize(hp-obj_pos[0].xyz);
			color = vec4(1.0, 0.4, 0.0, 0.0);
		}
		else if (mat < 2.5)
		{
			normal = normalize(hp-obj_pos[1].xyz);
			color = vec4(0.4, 1.0, 0.2, 0.0);
		}
		else if (mat < 3.5)
		{
			normal = normalize(hp-obj_pos[2].xyz);
			color = vec4(0.2, 0.3, 1.0, 0.0);
		}
		else if (mat < 4.5)
		{
			normal = -obj_pos[3].xyz;

			vec2 hpvec = hp.xz-obj_pos[2].xz;
			normal.xz += normalize(hpvec) * sin(length(hpvec) * 5.0 - iGlobalTime * 20.0) * 0.1 * boom;
			normal = normalize(normal);
			color = vec4(0.6, 0.6, 1.0, 0.0);
			vec2 cl = fract(hp.xz / 2.0) * 5.14159 * 2.0;
			float shad = 0.0;
			shad += sin(iGlobalTime*2.47+cl.x*1.3);
			shad += sin(iGlobalTime*2.96+cl.y*1.2);
			shad += sin(iGlobalTime*2.34-cl.x*1.44);
			shad += sin(iGlobalTime*2.56+cl.y*1.32);
			color *= clamp(shad * 0.1 + 0.5, 0.5, 1.0);
			// color *= cl.x*cl.y;
		}
		else if (mat < 5.5)
		{
			normal = -obj_pos[4].xyz;
			color = vec4(0.6, 0.8, 1.0, 0.0);
			vec2 cl = fract(hp.xy / 2.0);
			color *= cl.x*cl.y;
		}
		else if (mat < 6.5)
		{
			normal = -obj_pos[5].xyz;
			color = vec4(0.6, 0.8, 1.0, 0.0);
			vec2 cl = fract(hp.xy / 2.0);
			color *= cl.x*cl.y;
		}
		else if (mat < 7.5)
		{
			normal = -obj_pos[6].xyz;
			color = vec4(0.6, 0.8, 1.0, 0.0);
			vec2 cl = fract(hp.zy / 2.0);
			color *= cl.x*cl.y;
		}
		else if (mat < 8.5)
		{
			normal = -obj_pos[7].xyz;
			color = vec4(0.6, 0.8, 1.0, 0.0);
			vec2 cl = fract(hp.zy / 2.0);
			color *= cl.x*cl.y;
		}
		else if (mat < 9.5)
		{
			normal = -obj_pos[8].xyz;
			color = vec4(0.1, 0.4, 1.0, 0.0);
			vec2 cl = fract(hp.xz / 2.0);
		//	color *= cl.x*cl.y;
		}
		else if (mat < 10.5)
		{
			normal = normalize(hp-obj_pos[9].xyz);
			color = vec4(0.6, 0.3, 0.6, 0.0);
		}
		else if (mat < 11.5)
		{
			normal = normalize(hp-obj_pos[10].xyz);
			color = vec4(0.4, 0.5, 0.2, 0.0);
		}

		else if (mat < 12.5)
		{
			normal = normalize(hp-obj_pos[11].xyz);
			color = vec4(0.9, 1.0, 1.1, 0.0);
		}
		else if (mat < 13.5)
		{
			normal = normalize(hp-obj_pos[12].xyz);
			color = vec4(0.7, 1.0, 1.1, 0.0);
		}
		ray.o = hp;
	}

	return mat;
}

float shade(in float mat, inout ray_t ray, in vec3 normal, in vec4 color, inout vec4 outcol, inout vec4 refblendfac)
{

	if (mat < 0.5)
	{
		outcol += color * refblendfac;
		return 0.0;
	}

	vec4 thiscol = vec4(0.0);


	vec3 lightvec = lp-ray.o;
	float lightdist = length(lightvec);
	vec3 ld = lightvec / lightdist;

	float diffuse = clamp(dot(normal, ld), 0.0, 1.0);
	float specular = 0.5;

	ray_t shadowray;
	shadowray.o = ray.o;
	shadowray.d = ld;
	if (trage_shadow(shadowray) < lightdist)
	{
		diffuse = 0.0;
		specular = 0.0;
	}

	//outcol = vec4(normal * 0.5 + vec3(0.5), 1.0);
	
	vec3 ref_ray_d = ray.d - 2.0*dot(ray.d, normal) * normal;
	float ref_fac = pow(clamp(-dot(normal, ray.d), 0.0, 1.0), 2.0);
	vec4 new_refblendfac = color * ref_fac * refblendfac * 3.0;
		
//        ref_ray_d = -ref_ray_d;
		
//		ref_col = textureCube(iChannel0, ref_ray_d) * 1.0 * ref_fac;

		
	float ref_fac2 = ref_fac * specular;
	float spec_light = pow(clamp(dot(ld, ref_ray_d), 0.0, 1.0), 3.0);
	
	// diffuse lighting
//    	diffuse = 0.2;
	thiscol += color * diffuse;
	// specular hilight
	thiscol += spec_light * ref_fac2;




	lightvec = lp2-ray.o;
	lightdist = length(lightvec);
	ld = lightvec / lightdist;
	diffuse = clamp(dot(normal, ld) * lp2_amount / lightdist, 0.1, 1.0);
	shadowray.o = ray.o;
	shadowray.d = ld;
	if (trage_shadow2(shadowray) < lightdist)
	{
		diffuse = 0.1;
	}
	spec_light = pow(clamp(dot(ld, ref_ray_d), 0.0, 1.0), 3.0) * diffuse;
	thiscol += color * diffuse * vec4(0.2, 0.3, 1.0,1.0) * 5.0;
	thiscol += spec_light * ref_fac2 * vec4(0.2, 0.3, 1.0,1.0) * 5.0;








	// SMG lighting
	float smg_fac = pow(clamp(-dot(normal, ray.d), 0.0, 1.0), 2.0);
	thiscol += vec4(pow(1.0 - smg_fac, 10.0) * 0.3);
	
	// mix with previous stage and set up next
	outcol += thiscol * refblendfac;
	ray.d = ref_ray_d;
	refblendfac = new_refblendfac;

	return 1.0;
}

void trace(in ray_t ray, out vec4 outcol)
{
	vec3 hp;
	vec3 normal;
	vec4 color;
	float mat;

	float cont;

	ray_t refray;
	vec4 refblendfac;

	outcol = vec4(0.0);
	refray = ray;
	refblendfac = vec4(1.0);
	mat = trage(refray, normal, color);
	cont = shade(mat, refray, normal, color, outcol, refblendfac);


	if (cont > 0.5)
	{
		mat = trage(refray, normal, color);
		cont = shade(mat, refray, normal, color, outcol, refblendfac);
	}

	if (cont > 0.5)
	{
		mat = trage(refray, normal, color);
		cont = shade(mat, refray, normal, color, outcol, refblendfac);
	}

	if (cont > 0.5)
	{
		mat = trage(refray, normal, color);
		cont = shade(mat, refray, normal, color, outcol, refblendfac);
	}

	if (cont > 0.5)
	{
		mat = trage(refray, normal, color);
		cont = shade(mat, refray, normal, color, outcol, refblendfac);
	}

	if (cont > 0.5)
	{
		mat = trage(refray, normal, color);
		cont = shade(mat, refray, normal, color, outcol, refblendfac);
	}

}


void main(void)
{
	vec2 uv = gl_FragCoord.xy / iResolution.xy;
	
	uv.xy -= vec2(0.5);
    uv.x *= iResolution.z;
	uv.xy += vec2(0.5);

	float speed = 1.1;
	float ang = -1.2 + sin(iGlobalTime * 1.0432 * speed) * 0.1;
	float ang2 = 0.0 + sin(iGlobalTime * 1.21231 * speed) * 0.1;
	float ang2b = u_lookup;
	float ang3 = sin(iGlobalTime * 0.73223 * speed) - 0.6;

//    ang = 1.0;
	
	mat3 rotinv = mat3(
		cos(ang), 0.0, -sin(ang),
		0.0,      1.0, 0.0,
		sin(ang), 0.0, cos(ang));

	mat3 rotinv2 = mat3(
		  1.0, 0.0, 0.0,
		  0.0, cos(ang2), -sin(ang2),
		  0.0, sin(ang2), cos(ang2));

	mat3 rotinv2b = mat3(
		  1.0, 0.0, 0.0,
		  0.0, cos(ang2b), -sin(ang2b),
		  0.0, sin(ang2b), cos(ang2b));

		mat3 rotinv3 = mat3(
		  cos(ang3), -sin(ang3), 0.0,
		  sin(ang3), cos(ang3), 0.0,
		  0.0, 0.0, 1.0);


	mat3 scale = mat3(
	1.0, 0.0, 0.0,
	0.0, 0.3, 0.0,
	0.0, 0.0, 1.0);
 
	rotinv = mat3(1.0) * rotinv * rotinv2 * rotinv2b;
	
	mat3 rot = mat3(
		rotinv[0][0],rotinv[1][0],rotinv[2][0],
		rotinv[0][1],rotinv[1][1],rotinv[2][1],
		rotinv[0][2],rotinv[1][2],rotinv[2][2]);

	
	update_positions();

	ray_t ray;
	ray.o = vec3(6.0, -0.7 + sin(iGlobalTime * 2.1) * 0.5, -2.5 ) - normalize(vec3(-2.0, -0.3, 1.0)) * u_camdist;
	ray.d = vec3((uv.x - 0.5), uv.y - 0.5, 1.0);

	ray.o += campos_fudge;
	ray.d += camdir_fudge;
	ray.d = normalize(ray.d);

  //  ro += rd * ((0.5)/rd.z);
	
	ray.o = ray.o;
	ray.d = rotinv * ray.d;
	
	
	vec4 outcol;
	trace(ray, outcol);

	gl_FragColor = outcol;
}