/*
 * Some raymarching by rimina
 */

THREE.Lightt = {

	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: 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 64",
		"#define E 0.001",
		"#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 = 333.0;",

		"struct Material{",
		  "vec3 lamb;",
		  "vec3 spec;",
		  "float shiny;",
		"};",

		"Material M;",

		"Material getMaterial(int id){",
		  "Material m;",
		  
		  "m.lamb = vec3(0.8, 0.5, 0.2);",
		  "m.spec = vec3(0.8, 0.5, 0.2)*1.25;",
		  "m.shiny = 20.0;",
		  
		  "if(id == 0){",
		    "m.lamb = vec3(0.0);",
		    "m.spec = m.lamb;",
		    "m.shiny = 100.0;",
		  "}",
		  
		  "return m;",
		"}",

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

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

		"float scene(vec3 p){",
		  "vec3 pp = p;",
		  "for(int i = 0; i < 5; ++i){",
		    "pp = abs(pp) - vec3(0.75, 2.0, 1.5);",
		    "rot(pp.xz, uHb*0.1);",
		    "rot(pp.zy, uHb*2.0);",
		    "rot(pp.xy, uHb*0.5+uHb*0.1);",
		  "}",
		  
		  "float cubes = box(pp, vec3(5.0, 0.2, 5.0));",
		  
		  "glow += vec3(0.8, 0.25, 0.15) * 0.05 / (abs(cubes) + 0.01);",
		  "cubes = max(cubes, 0.5);",
		   "M = getMaterial(0);",
		  
		  "return cubes;",
		"}",

		"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 rd, vec3 p, vec3 ld, Material m){",
		  "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, m.shiny);",
		  
		  "return m.lamb*l + m.spec*s;",
		"}",

        "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;",
            "vec3 ld = normalize(vec3(0.0, 0.0, -10.0)-p);",
  			"Material m = M;",

			"if(t < FAR){",
				"col = shade(rd, p, ld, m);",
			"}",
			"col += glow*0.5;",

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

	].join("\n")

};
