/*
 * Some raymarching by rimina
 */

THREE.Kaleido2 = {

	uniforms: {
		"uNear": { type: "f", value: 0 },
		"uFar":   { type: "f", value: 0 },
		"uRo": { type: "v3", value: new THREE.Vector3(0, 0, 0)},
		"uRd": { type: "v3", value: new THREE.Vector3(0, 0, 0)},
		"uTime" : { type: "f", value: 0.0},
		"uHb" : {type: "f", value: 0.0},
		"uResolution" : {type: "v2", value: new THREE.Vector2(0, 0)},
		"uFOV" : {type : "f", value: 50.0}
	},

	vertexShader: [

		"varying vec2 vUv;",
		"void main() {",
			"vUv = uv;",
			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
		"}"

	].join("\n"),

	fragmentShader: [
		"#include <packing>",
        "uniform float uNear;",
		"uniform float uFar;",
		"uniform vec3 uRo;",
		"uniform vec3 uRd;",

		"uniform float uTime;",
		"uniform float uHb;",
		"uniform vec2 uResolution;",
		"uniform float uFOV;",

        "varying vec2 vUv;",

        "#define STEPS 60",
		"#define E 0.01",
		"#define FAR 200.0",

		"const vec3 FOG_COLOR = vec3(0.015, 0.03, 0.03);",
		"const vec3 LIGHT_COLOR = vec3(0.8, 0.25, 0.15);",
		"vec3 glow = vec3(0.0);",

		"const float BPM = 92.0;",

		"float fSphere(vec3 p, float r) {",
			"return length(p) - r;",
		"}",

		"void rot(inout vec2 p, float a){",
			"p = cos(a)*p + sin(a)*vec2(p.y, -p.x);",
		"}",

		// 3D noise function (IQ)
		"float noise(vec3 p){",
			"vec3 ip = floor(p);",
		    "p -= ip;",
		    "vec3 s = vec3(7.0,157.0,113.0);",
		    "vec4 h = vec4(0.0, s.yz, s.y+s.z)+dot(ip, s);",
		    "p = p*p*(3.0-2.0*p);",
		    "h = mix(fract(sin(h)*43758.5), fract(sin(h+s.x)*43758.5), p.x);",
		    "h.xy = mix(h.xz, h.yw, p.y);",
		    "return mix(h.x, h.y, p.z);",
		"}",

		"float scene(vec3 p){",
			"vec3 pp = p;",

			"float n = noise(p-uTime*2.0);",

			"float orb = fSphere(p, 5.0+uHb*2.0)-n*0.9;",

			"rot(pp.xy, uTime*0.2);",
			"for(int i = 0; i < 5; ++i){",
				"pp = abs(pp) - (vec3(0.9, 0.8, 1.0)+uHb);",
				"rot(pp.xy, uTime*0.05);",
				"rot(pp.xz, uTime*0.25);",
				"rot(pp.xy, uTime*0.1);",
				
			"}",
			"pp -= n*0.9;",
			"float a = fSphere(pp, 1.5);",
			"float b = fSphere(pp, 0.2);",

		    "vec3 g = vec3(0.5, 0.05, 0.01) * 0.05 / (abs(a) + 0.01);",
			"g += vec3(0.5, 0.0, 0.01) * 0.05 / (abs(orb) + 0.01);",
			"g += vec3(0.5, 0.0, 0.0) * 0.03 / (abs(b) + 0.01);",
			"g *= 0.33;",
		    "glow += g;",

			"orb = max(abs(orb), 0.5);",
			"a = max(abs(a), 0.02);",
			"return min(min(a, b), orb);",
		"}",

		"float march(vec3 ro, vec3 rd){",
		    "float t = E;",
		    "vec3 p = ro;",
		    "for(int i = 0; i < STEPS; ++i){",
		        "float d = scene(p);",
				"t += d;",
		        "p = ro + rd*t;",
		        
		        "if(d < E || t > FAR){",
		            "break;",
		        "}",
		    "}",
		    "return t;",
		"}",

		"vec3 normals(vec3 p){",
			"vec3 e = vec3(E, 0.0, 0.0);",
			"return normalize(vec3(",
			  "scene(p+e.xyy) - scene(p-e.xyy),",
			  "scene(p+e.yxy) - scene(p-e.yxy),",
			  "scene(p+e.yyx) - scene(p-e.yyx)",
			"));",
		"}",

		"vec3 shade(vec3 p, vec3 rd, vec3 ld){",	
			"vec3 n = normals(p);",
			
			"float l = max(dot(n, ld), 0.0);",
			"vec3 a = reflect(n, ld);",
			"float s = pow(max(dot(rd, a), 0.0), 10.0);",

			"vec3 lc = vec3(0.8, 0.5, 0.2);",
  			"vec3 sc= vec3(0.8, 0.2, 0.4);",
			
			"return lc*l*0.5 + sc*s*0.8;",
		"}",

		//http://www.iquilezles.org/www/articles/fog/fog.htm
		"vec3 fog(vec3 col, vec3 p, vec3 ro, vec3 rd, vec3 ld){",
			"float dist = length(p-ro);",
			"float sunAmount = max( dot(rd, -ld), 0.0 );",
			"float fogAmount = 1.0 - exp( -dist*0.09);",
			"vec3  fogColor = mix(FOG_COLOR, LIGHT_COLOR, pow(sunAmount, 5.0-(fract(uTime*BPM)*(0.1+2.0*smoothstep(30.00, 120.00, uTime)))));",
			"return mix(col, fogColor, fogAmount);",
		"}",
  

        "void main() {",

        	"vec2 q = -1.0+2.0*vUv;",
    		"q.x *= uResolution.x/uResolution.y;",

            "vec3 col = vec3(0.0);",

            "vec3 z = normalize(uRd - uRo);",
            "vec3 x = normalize(cross(z, vec3(0.0, 1.0, 0.0)));",
    		"vec3 y = normalize(cross(x, z));",
    		"vec3 rd = normalize(mat3(x, y, z)*vec3(q, 1.0/radians(uFOV)));",

            "float t = march(uRo, rd);",
            "vec3 p = uRo + rd*t;",
			"if(t < FAR){",
				"col = shade(p, rd, -z);",
			"}",
			"col *= glow;",
			"col = fog(col, p, uRo, rd, -z);",

            "gl_FragColor = vec4(col, 1.0);",
        "}"

	].join("\n")

};
