/*
 * Some raymarching by rimina
 */

THREE.Universe = {

	uniforms: {
		"uNear": { type: "f", value: 0.01 },
		"uFar":   { type: "f", value: 180 },
		"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: 60.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 100",
		"#define E 0.001",

		"const vec3 FOG_COLOR = vec3(0.0);",
		"const vec3 LIGHT_COLOR = vec3(0.3, 0.6, 0.9);",
		"vec3 glow = vec3(0.0);",

		"float ID = 0.0;",

		"const float BPM = 120.0;",

		"float box(vec3 p, vec3 b){",
			"vec3 d = abs(p)-b;",
			"return length(max(d, 0.0)) + min(max(d.x, max(d.y, d.z)), 0.0);",
		"}",

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

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

		"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 safe = sphere(pp, 10.0);",
			"rotate(pp.xz, uHb);",
			"rotate(pp.yz, uHb*0.25);",
			"for(int i = 0; i < 7; ++i){",
				"pp = abs(pp) - (vec3(.4, .1, .5)+(10.0-uHb)*0.25);",

				"rotate(pp.xy, (10.0-uHb)*0.25);",
				"rotate(pp.xz, uHb*0.5);",
				"rotate(pp.yz, uHb);",
			"}",

			"pp -= noise(p-uTime*2.0)*0.9;",
			"float a = box(pp, vec3(0.5, 1.5, 0.5));",
			"a = max(a, -safe);",

			"pp -= noise(p+uHb*2.5)*0.4;",

			"rotate(pp.xy, uHb);",

			"float offset = 12.0;",

			"ID = floor((pp.z + offset*0.5) / offset);",
			"pp.z = mod(pp.z + offset*0.5, offset)-offset*0.5;",

			"float b = box(pp, vec3(0.5, 10., 1.5));",

			"vec3 lc = vec3(0.3, 0.5, 0.8)*0.25;",
			"vec3 sc = vec3(0.8, 0.4, 0.2)*0.25;",

			"if(mod(ID, 8.0) == 0.0){",
				"lc = lc.bgr;",
				"sc = sc.bgr;",
			"}",

			"vec3 g = sc * 0.01 / (abs(a) + 0.1);",
			"g += lc * 0.01 / (abs(b) + 0.1);",
			"glow += g*0.5;",

			"b = max(abs(b), 0.05);",
			"a = max(abs(a), 0.025);",

			"return min(a, b);",
		"}",

		"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 > uFar){",
		            "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 rd, vec3 p, vec3 ld){",
			"vec3 n = normals(p);",

			"float l = max(dot(n, ld), 0.0);",
			"float a = max(dot(reflect(rd, ld), n), 0.0);",
			"float s = pow(a, 5.0);",

			"vec3 lc = vec3(0.1, 0.5, 0.8);",
			"vec3 sc = vec3(0.3, 0.0, 0.9);",

			"if(mod(ID, 2.0) == 0.0){",
				"lc = lc.brg;",
				"sc = sc.rbg;",
			"}",

			"return l * lc * 0.5 + s * sc * 0.8;",
		"}",

        "void main() {",

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

            "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)));",

    		"vec3 col = vec3(0.0);",
            "float t = march(uRo, rd);",
            "vec3 p = uRo + rd*t;",
			"vec3 ld = -z;",
			"if(t < uFar){",
				"col = shade(rd, p, ld);",
			"}",

			"col += glow*0.35;",

			"col = smoothstep(-0.2, 1.2, col);",

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

	].join("\n")

};
