(function(){ "use strict"; var Delaunay; (function() { var EPSILON = 1.0 / 1048576.0; function supertriangle(vertices) { var xmin = Number.POSITIVE_INFINITY, ymin = Number.POSITIVE_INFINITY, xmax = Number.NEGATIVE_INFINITY, ymax = Number.NEGATIVE_INFINITY, i, dx, dy, dmax, xmid, ymid; for(i = vertices.length; i--; ) { if(vertices[i][0] < xmin) xmin = vertices[i][0]; if(vertices[i][0] > xmax) xmax = vertices[i][0]; if(vertices[i][1] < ymin) ymin = vertices[i][1]; if(vertices[i][1] > ymax) ymax = vertices[i][1]; } dx = xmax - xmin; dy = ymax - ymin; dmax = Math.max(dx, dy); xmid = xmin + dx * 0.5; ymid = ymin + dy * 0.5; return [ [xmid - 20 * dmax, ymid - dmax], [xmid , ymid + 20 * dmax], [xmid + 20 * dmax, ymid - dmax] ]; } function circumcircle(vertices, i, j, k) { var x1 = vertices[i][0], y1 = vertices[i][1], x2 = vertices[j][0], y2 = vertices[j][1], x3 = vertices[k][0], y3 = vertices[k][1], fabsy1y2 = Math.abs(y1 - y2), fabsy2y3 = Math.abs(y2 - y3), xc, yc, m1, m2, mx1, mx2, my1, my2, dx, dy; /* Check for coincident points */ if(fabsy1y2 < EPSILON && fabsy2y3 < EPSILON) throw new Error("Eek! Coincident points!"); if(fabsy1y2 < EPSILON) { m2 = -((x3 - x2) / (y3 - y2)); mx2 = (x2 + x3) / 2.0; my2 = (y2 + y3) / 2.0; xc = (x2 + x1) / 2.0; yc = m2 * (xc - mx2) + my2; } else if(fabsy2y3 < EPSILON) { m1 = -((x2 - x1) / (y2 - y1)); mx1 = (x1 + x2) / 2.0; my1 = (y1 + y2) / 2.0; xc = (x3 + x2) / 2.0; yc = m1 * (xc - mx1) + my1; } else { m1 = -((x2 - x1) / (y2 - y1)); m2 = -((x3 - x2) / (y3 - y2)); mx1 = (x1 + x2) / 2.0; mx2 = (x2 + x3) / 2.0; my1 = (y1 + y2) / 2.0; my2 = (y2 + y3) / 2.0; xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2); yc = (fabsy1y2 > fabsy2y3) ? m1 * (xc - mx1) + my1 : m2 * (xc - mx2) + my2; } dx = x2 - xc; dy = y2 - yc; return {i: i, j: j, k: k, x: xc, y: yc, r: dx * dx + dy * dy}; } function dedup(edges) { var i, j, a, b, m, n; for(j = edges.length; j; ) { b = edges[--j]; a = edges[--j]; for(i = j; i; ) { n = edges[--i]; m = edges[--i]; if((a === m && b === n) || (a === n && b === m)) { edges.splice(j, 2); edges.splice(i, 2); break; } } } } Delaunay = { triangulate: function(vertices, key) { var n = vertices.length, i, j, indices, st, open, closed, edges, dx, dy, a, b, c; /* Bail if there aren't enough vertices to form any triangles. */ if(n < 3) return []; /* Slice out the actual vertices from the passed objects. (Duplicate the * array even if we don't, though, since we need to make a supertriangle * later on!) */ vertices = vertices.slice(0); if(key) for(i = n; i--; ) vertices[i] = vertices[i][key]; /* Make an array of indices into the vertex array, sorted by the * vertices' x-position. */ indices = new Array(n); for(i = n; i--; ) indices[i] = i; indices.sort(function(i, j) { return vertices[j][0] - vertices[i][0]; }); /* Next, find the vertices of the supertriangle (which contains all other * triangles), and append them onto the end of a (copy of) the vertex * array. */ st = supertriangle(vertices); vertices.push(st[0], st[1], st[2]); /* Initialize the open list (containing the supertriangle and nothing * else) and the closed list (which is empty since we havn't processed * any triangles yet). */ open = [circumcircle(vertices, n + 0, n + 1, n + 2)]; closed = []; edges = []; /* Incrementally add each vertex to the mesh. */ for(i = indices.length; i--; edges.length = 0) { c = indices[i]; /* For each open triangle, check to see if the current point is * inside it's circumcircle. If it is, remove the triangle and add * it's edges to an edge list. */ for(j = open.length; j--; ) { /* If this point is to the right of this triangle's circumcircle, * then this triangle should never get checked again. Remove it * from the open list, add it to the closed list, and skip. */ dx = vertices[c][0] - open[j].x; if(dx > 0.0 && dx * dx > open[j].r) { closed.push(open[j]); open.splice(j, 1); continue; } /* If we're outside the circumcircle, skip this triangle. */ dy = vertices[c][1] - open[j].y; if(dx * dx + dy * dy - open[j].r > EPSILON) continue; /* Remove the triangle and add it's edges to the edge list. */ edges.push( open[j].i, open[j].j, open[j].j, open[j].k, open[j].k, open[j].i ); open.splice(j, 1); } /* Remove any doubled edges. */ dedup(edges); /* Add a new triangle for each edge. */ for(j = edges.length; j; ) { b = edges[--j]; a = edges[--j]; open.push(circumcircle(vertices, a, b, c)); } } /* Copy any remaining open triangles to the closed list, and then * remove any triangles that share a vertex with the supertriangle, * building a list of triplets that represent triangles. */ for(i = open.length; i--; ) closed.push(open[i]); open.length = 0; for(i = closed.length; i--; ) if(closed[i].i < n && closed[i].j < n && closed[i].k < n) open.push(closed[i].i, closed[i].j, closed[i].k); /* Yay, we're done! */ return open; }, contains: function(tri, p) { /* Bounding box test first, for quick rejections. */ if((p[0] < tri[0][0] && p[0] < tri[1][0] && p[0] < tri[2][0]) || (p[0] > tri[0][0] && p[0] > tri[1][0] && p[0] > tri[2][0]) || (p[1] < tri[0][1] && p[1] < tri[1][1] && p[1] < tri[2][1]) || (p[1] > tri[0][1] && p[1] > tri[1][1] && p[1] > tri[2][1])) return null; var a = tri[1][0] - tri[0][0], b = tri[2][0] - tri[0][0], c = tri[1][1] - tri[0][1], d = tri[2][1] - tri[0][1], i = a * d - b * c; /* Degenerate tri. */ if(i === 0.0) return null; var u = (d * (p[0] - tri[0][0]) - b * (p[1] - tri[0][1])) / i, v = (a * (p[1] - tri[0][1]) - c * (p[0] - tri[0][0])) / i; /* If we're outside the tri, fail. */ if(u < 0.0 || v < 0.0 || (u + v) > 1.0) return null; return [u, v]; } }; if(typeof module !== "undefined") module.exports = Delaunay; })(); var FSS = { FRONT : 0, BACK : 1, DOUBLE : 2, SVGNS : 'http://www.w3.org/2000/svg' }; /** * @class Array * @author Matthew Wagerfield */ FSS.Array = typeof Float32Array === 'function' ? Float32Array : Array; /** * @class Utils * @author Matthew Wagerfield */ FSS.Utils = { isNumber: function(value) { return !isNaN(parseFloat(value)) && isFinite(value); } }; /** * @object Math Augmentation * @author Matthew Wagerfield */ Math.PIM2 = Math.PI*2; Math.PID2 = Math.PI/2; Math.randomInRange = function(min, max) { return min + (max - min) * Math.random(); }; Math.clamp = function(value, min, max) { value = Math.max(value, min); value = Math.min(value, max); return value; }; /** * @object Vector3 * @author Matthew Wagerfield */ FSS.Vector3 = { create: function(x, y, z) { var vector = new FSS.Array(3); this.set(vector, x, y, z); return vector; }, clone: function(a) { var vector = this.create(); this.copy(vector, a); return vector; }, set: function(target, x, y, z) { target[0] = x || 0; target[1] = y || 0; target[2] = z || 0; return this; }, setX: function(target, x) { target[0] = x || 0; return this; }, setY: function(target, y) { target[1] = y || 0; return this; }, setZ: function(target, z) { target[2] = z || 0; return this; }, copy: function(target, a) { target[0] = a[0]; target[1] = a[1]; target[2] = a[2]; return this; }, add: function(target, a) { target[0] += a[0]; target[1] += a[1]; target[2] += a[2]; return this; }, addVectors: function(target, a, b) { target[0] = a[0] + b[0]; target[1] = a[1] + b[1]; target[2] = a[2] + b[2]; return this; }, addScalar: function(target, s) { target[0] += s; target[1] += s; target[2] += s; return this; }, subtract: function(target, a) { target[0] -= a[0]; target[1] -= a[1]; target[2] -= a[2]; return this; }, subtractVectors: function(target, a, b) { target[0] = a[0] - b[0]; target[1] = a[1] - b[1]; target[2] = a[2] - b[2]; return this; }, subtractScalar: function(target, s) { target[0] -= s; target[1] -= s; target[2] -= s; return this; }, multiply: function(target, a) { target[0] *= a[0]; target[1] *= a[1]; target[2] *= a[2]; return this; }, multiplyVectors: function(target, a, b) { target[0] = a[0] * b[0]; target[1] = a[1] * b[1]; target[2] = a[2] * b[2]; return this; }, multiplyScalar: function(target, s) { target[0] *= s; target[1] *= s; target[2] *= s; return this; }, divide: function(target, a) { target[0] /= a[0]; target[1] /= a[1]; target[2] /= a[2]; return this; }, divideVectors: function(target, a, b) { target[0] = a[0] / b[0]; target[1] = a[1] / b[1]; target[2] = a[2] / b[2]; return this; }, divideScalar: function(target, s) { if (s !== 0) { target[0] /= s; target[1] /= s; target[2] /= s; } else { target[0] = 0; target[1] = 0; target[2] = 0; } return this; }, cross: function(target, a) { var x = target[0]; var y = target[1]; var z = target[2]; target[0] = y*a[2] - z*a[1]; target[1] = z*a[0] - x*a[2]; target[2] = x*a[1] - y*a[0]; return this; }, crossVectors: function(target, a, b) { target[0] = a[1]*b[2] - a[2]*b[1]; target[1] = a[2]*b[0] - a[0]*b[2]; target[2] = a[0]*b[1] - a[1]*b[0]; return this; }, min: function(target, value) { if (target[0] < value) { target[0] = value; } if (target[1] < value) { target[1] = value; } if (target[2] < value) { target[2] = value; } return this; }, max: function(target, value) { if (target[0] > value) { target[0] = value; } if (target[1] > value) { target[1] = value; } if (target[2] > value) { target[2] = value; } return this; }, clamp: function(target, min, max) { this.min(target, min); this.max(target, max); return this; }, limit: function(target, min, max) { var length = this.length(target); if (min !== null && length < min) { this.setLength(target, min); } else if (max !== null && length > max) { this.setLength(target, max); } return this; }, dot: function(a, b) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }, normalise: function(target) { return this.divideScalar(target, this.length(target)); }, negate: function(target) { return this.multiplyScalar(target, -1); }, distanceSquared: function(a, b) { var dx = a[0] - b[0]; var dy = a[1] - b[1]; var dz = a[2] - b[2]; return dx*dx + dy*dy + dz*dz; }, distance: function(a, b) { return Math.sqrt(this.distanceSquared(a, b)); }, lengthSquared: function(a) { return a[0]*a[0] + a[1]*a[1] + a[2]*a[2]; }, length: function(a) { return Math.sqrt(this.lengthSquared(a)); }, setLength: function(target, l) { var length = this.length(target); if (length !== 0 && l !== length) { this.multiplyScalar(target, l / length); } return this; } }; /** * @object Vector4 * @author Matthew Wagerfield */ FSS.Vector4 = { create: function(x, y, z, w) { var vector = new FSS.Array(4); this.set(vector, x, y, z); return vector; }, set: function(target, x, y, z, w) { target[0] = x || 0; target[1] = y || 0; target[2] = z || 0; target[3] = w || 0; return this; }, setX: function(target, x) { target[0] = x || 0; return this; }, setY: function(target, y) { target[1] = y || 0; return this; }, setZ: function(target, z) { target[2] = z || 0; return this; }, setW: function(target, w) { target[3] = w || 0; return this; }, add: function(target, a) { target[0] += a[0]; target[1] += a[1]; target[2] += a[2]; target[3] += a[3]; return this; }, multiplyVectors: function(target, a, b) { target[0] = a[0] * b[0]; target[1] = a[1] * b[1]; target[2] = a[2] * b[2]; target[3] = a[3] * b[3]; return this; }, multiplyScalar: function(target, s) { target[0] *= s; target[1] *= s; target[2] *= s; target[3] *= s; return this; }, min: function(target, value) { if (target[0] < value) { target[0] = value; } if (target[1] < value) { target[1] = value; } if (target[2] < value) { target[2] = value; } if (target[3] < value) { target[3] = value; } return this; }, max: function(target, value) { if (target[0] > value) { target[0] = value; } if (target[1] > value) { target[1] = value; } if (target[2] > value) { target[2] = value; } if (target[3] > value) { target[3] = value; } return this; }, clamp: function(target, min, max) { this.min(target, min); this.max(target, max); return this; } }; /** * @class Color * @author Matthew Wagerfield */ FSS.Color = function(hex, opacity) { this.rgba = FSS.Vector4.create(); this.hex = hex || '#000000'; this.opacity = FSS.Utils.isNumber(opacity) ? opacity : 1; this.set(this.hex, this.opacity); }; FSS.Color.prototype = { set: function(hex, opacity) { hex = hex.replace('#', ''); var size = hex.length / 3; this.rgba[0] = parseInt(hex.substring(size*0, size*1), 16) / 255; this.rgba[1] = parseInt(hex.substring(size*1, size*2), 16) / 255; this.rgba[2] = parseInt(hex.substring(size*2, size*3), 16) / 255; this.rgba[3] = FSS.Utils.isNumber(opacity) ? opacity : this.rgba[3]; return this; }, hexify: function(channel) { var hex = Math.ceil(channel*255).toString(16); if (hex.length === 1) { hex = '0' + hex; } return hex; }, format: function() { var r = this.hexify(this.rgba[0]); var g = this.hexify(this.rgba[1]); var b = this.hexify(this.rgba[2]); this.hex = '#' + r + g + b; return this.hex; } }; /** * @class Object * @author Matthew Wagerfield */ FSS.Object = function() { this.position = FSS.Vector3.create(); }; FSS.Object.prototype = { setPosition: function(x, y, z) { FSS.Vector3.set(this.position, x, y, z); return this; } }; /** * @class Light * @author Matthew Wagerfield */ FSS.Light = function(ambient, diffuse) { FSS.Object.call(this); this.ambient = new FSS.Color(ambient); this.diffuse = new FSS.Color(diffuse); this.ray = FSS.Vector3.create(); }; FSS.Light.prototype = Object.create(FSS.Object.prototype); /** * @class Vertex * @author Matthew Wagerfield */ FSS.Vertex = function(x, y, z) { this.position = FSS.Vector3.create(x, y, z); }; /** * @class Triangle * @author Matthew Wagerfield */ FSS.Triangle = function(a, b, c) { this.a = a || new FSS.Vertex(); this.b = b || new FSS.Vertex(); this.c = c || new FSS.Vertex(); this.vertices = [this.a, this.b, this.c]; this.u = FSS.Vector3.create(); this.v = FSS.Vector3.create(); this.centroid = FSS.Vector3.create(); this.normal = FSS.Vector3.create(); this.color = new FSS.Color(); this.polygon = document.createElementNS(FSS.SVGNS, 'polygon'); this.polygon.setAttributeNS(null, 'stroke-linejoin', 'round'); this.polygon.setAttributeNS(null, 'stroke-miterlimit', '1'); this.polygon.setAttributeNS(null, 'stroke-width', '1'); this.computeCentroid(); this.computeNormal(); }; FSS.Triangle.prototype = { computeCentroid: function() { this.centroid[0] = this.a.position[0] + this.b.position[0] + this.c.position[0]; this.centroid[1] = this.a.position[1] + this.b.position[1] + this.c.position[1]; this.centroid[2] = this.a.position[2] + this.b.position[2] + this.c.position[2]; FSS.Vector3.divideScalar(this.centroid, 3); return this; }, computeNormal: function() { FSS.Vector3.subtractVectors(this.u, this.b.position, this.a.position); FSS.Vector3.subtractVectors(this.v, this.c.position, this.a.position); FSS.Vector3.crossVectors(this.normal, this.u, this.v); FSS.Vector3.normalise(this.normal); return this; } }; /** * @class Geometry * @author Matthew Wagerfield */ FSS.Geometry = function() { this.vertices = []; this.triangles = []; this.dirty = false; }; FSS.Geometry.prototype = { update: function() { if (this.dirty) { var t,triangle; for (t = this.triangles.length - 1; t >= 0; t--) { triangle = this.triangles[t]; triangle.computeCentroid(); triangle.computeNormal(); } this.dirty = false; } return this; } }; /** * @class Plane * @author Matthew Wagerfield, modified by Maksim Surguy to implement Delaunay triangulation */ FSS.Plane = function(width, height, howmany) { FSS.Geometry.call(this); this.width = width || 100; this.height = height || 100; // Cache Variables var x, y, vertices = new Array(howmany), offsetX = this.width * -0.5, offsetY = this.height * 0.5; for(i = vertices.length; i--; ) { x = offsetX + Math.random()*width; y = offsetY - Math.random()*height; vertices[i] = [x, y]; } // Generate additional points on the perimeter so that there are no holes in the pattern vertices.push([offsetX, offsetY]); vertices.push([offsetX + width/2, offsetY]); vertices.push([offsetX + width, offsetY]); vertices.push([offsetX + width, offsetY - height/2]); vertices.push([offsetX + width, offsetY - height]); vertices.push([offsetX + width/2, offsetY - height]); vertices.push([offsetX, offsetY - height]); vertices.push([offsetX, offsetY - height/2]); // Generate additional randomly placed points on the perimeter for (var i = 6; i >= 0; i--) { vertices.push([ offsetX + Math.random()*width, offsetY]); vertices.push([ offsetX, offsetY - Math.random()*height]); vertices.push([ offsetX + width, offsetY - Math.random()*height]); vertices.push([ offsetX + Math.random()*width, offsetY-height]); } // Create an array of triangulated coordinates from our vertices var triangles = Delaunay.triangulate(vertices); for(i = triangles.length; i; ) { --i; var p1 = [Math.ceil(vertices[triangles[i]][0]), Math.ceil(vertices[triangles[i]][1])]; --i; var p2 = [Math.ceil(vertices[triangles[i]][0]), Math.ceil(vertices[triangles[i]][1])]; --i; var p3 = [Math.ceil(vertices[triangles[i]][0]), Math.ceil(vertices[triangles[i]][1])]; var t1 = new FSS.Triangle(new FSS.Vertex(p1[0],p1[1]), new FSS.Vertex(p2[0],p2[1]), new FSS.Vertex(p3[0],p3[1])); this.triangles.push(t1); } }; FSS.Plane.prototype = Object.create(FSS.Geometry.prototype); /** * @class Material * @author Matthew Wagerfield */ FSS.Material = function(ambient, diffuse) { this.ambient = new FSS.Color(ambient || '#444444'); this.diffuse = new FSS.Color(diffuse || '#FFFFFF'); this.slave = new FSS.Color(); }; /** * @class Mesh * @author Matthew Wagerfield */ FSS.Mesh = function(geometry, material) { FSS.Object.call(this); this.geometry = geometry || new FSS.Geometry(); this.material = material || new FSS.Material(); this.side = FSS.FRONT; this.visible = true; }; FSS.Mesh.prototype = Object.create(FSS.Object.prototype); FSS.Mesh.prototype.update = function(lights, calculate) { var t,triangle, l,light, illuminance; // Update Geometry this.geometry.update(); // Calculate the triangle colors if (calculate) { // Iterate through Triangles for (t = this.geometry.triangles.length - 1; t >= 0; t--) { triangle = this.geometry.triangles[t]; // Reset Triangle Color FSS.Vector4.set(triangle.color.rgba); // Iterate through Lights for (l = lights.length - 1; l >= 0; l--) { light = lights[l]; // Calculate Illuminance FSS.Vector3.subtractVectors(light.ray, light.position, triangle.centroid); FSS.Vector3.normalise(light.ray); illuminance = FSS.Vector3.dot(triangle.normal, light.ray); if (this.side === FSS.FRONT) { illuminance = Math.max(illuminance, 0); } else if (this.side === FSS.BACK) { illuminance = Math.abs(Math.min(illuminance, 0)); } else if (this.side === FSS.DOUBLE) { illuminance = Math.max(Math.abs(illuminance), 0); } // Calculate Ambient Light FSS.Vector4.multiplyVectors(this.material.slave.rgba, this.material.ambient.rgba, light.ambient.rgba); FSS.Vector4.add(triangle.color.rgba, this.material.slave.rgba); // Calculate Diffuse Light FSS.Vector4.multiplyVectors(this.material.slave.rgba, this.material.diffuse.rgba, light.diffuse.rgba); FSS.Vector4.multiplyScalar(this.material.slave.rgba, illuminance); FSS.Vector4.add(triangle.color.rgba, this.material.slave.rgba); } // Clamp & Format Color FSS.Vector4.clamp(triangle.color.rgba, 0, 1); } } return this; }; /** * @class Scene * @author Matthew Wagerfield */ FSS.Scene = function() { this.meshes = []; this.lights = []; }; FSS.Scene.prototype = { add: function(object) { if (object instanceof FSS.Mesh && !~this.meshes.indexOf(object)) { this.meshes.push(object); } else if (object instanceof FSS.Light && !~this.lights.indexOf(object)) { this.lights.push(object); } return this; } }; /** * @class Renderer * @author Matthew Wagerfield */ FSS.Renderer = function() { this.width = 0; this.height = 0; this.halfWidth = 0; this.halfHeight = 0; }; FSS.Renderer.prototype = { setSize: function(width, height) { if (this.width === width && this.height === height) return; this.width = width; this.height = height; this.halfWidth = this.width * 0.5; this.halfHeight = this.height * 0.5; return this; }, clear: function() { return this; }, render: function(scene) { return this; } }; /** * @class Canvas Renderer * @author Matthew Wagerfield */ FSS.CanvasRenderer = function() { FSS.Renderer.call(this); this.element = document.createElement('canvas'); this.element.style.display = 'block'; this.context = this.element.getContext('2d'); this.setSize(this.element.width, this.element.height); }; FSS.CanvasRenderer.prototype = Object.create(FSS.Renderer.prototype); FSS.CanvasRenderer.prototype.setSize = function(width, height) { FSS.Renderer.prototype.setSize.call(this, width, height); this.element.width = width; this.element.height = height; this.context.setTransform(1, 0, 0, -1, this.halfWidth, this.halfHeight); return this; }; FSS.CanvasRenderer.prototype.clear = function() { FSS.Renderer.prototype.clear.call(this); this.context.clearRect(-this.halfWidth, -this.halfHeight, this.width, this.height); return this; }; FSS.CanvasRenderer.prototype.render = function(scene) { FSS.Renderer.prototype.render.call(this, scene); var m,mesh, t,triangle, color; // Clear Context this.clear(); // Configure Context this.context.lineJoin = 'round'; this.context.lineWidth = 1; // Update Meshes for (m = scene.meshes.length - 1; m >= 0; m--) { mesh = scene.meshes[m]; if (mesh.visible) { mesh.update(scene.lights, true); // Render Triangles for (t = mesh.geometry.triangles.length - 1; t >= 0; t--) { triangle = mesh.geometry.triangles[t]; color = triangle.color.format(); this.context.beginPath(); this.context.moveTo(triangle.a.position[0], triangle.a.position[1]); this.context.lineTo(triangle.b.position[0], triangle.b.position[1]); this.context.lineTo(triangle.c.position[0], triangle.c.position[1]); this.context.closePath(); this.context.strokeStyle = color; this.context.fillStyle = color; this.context.stroke(); this.context.fill(); } } } return this; }; //------------------------------ // Mesh Properties //------------------------------ var MESH = { width: 1.2, height: 1.2, slices: 250, ambient: '#555555', diffuse: '#FFFFFF' }; //------------------------------ // Light Properties //------------------------------ var LIGHT = { count: 1, xPos : 0, yPos : 200, zOffset: 100, ambient: '#123066', diffuse: '#567800', pickedup :true, proxy : false }; //------------------------------ // Render Properties //------------------------------ var CANVAS = 'canvas'; var RENDER = { renderer: CANVAS }; //------------------------------ // Global Properties //------------------------------ var center = FSS.Vector3.create(); var container = document.getElementById('background-effect'); var output = document.getElementById('background-effect'); var renderer, scene, mesh, geometry, material; var canvasRenderer; var gui; //------------------------------ // Methods //------------------------------ function initialise() { createRenderer(); createScene(); createMesh(); addLight(); addEventListeners(); resize(container.offsetWidth, container.offsetHeight); animate(); } function createRenderer() { canvasRenderer = new FSS.CanvasRenderer(); setRenderer(RENDER.renderer); } function setRenderer(index) { renderer = canvasRenderer; renderer.setSize(container.offsetWidth, container.offsetHeight); output.appendChild(renderer.element); } function createScene() { scene = new FSS.Scene(); } function createMesh() { renderer.clear(); geometry = new FSS.Plane(MESH.width * renderer.width, MESH.height * renderer.height, MESH.slices); material = new FSS.Material(MESH.ambient, MESH.diffuse); mesh = new FSS.Mesh(geometry, material); scene.add(mesh); } // Add a single light function addLight() { renderer.clear(); var light = new FSS.Light(LIGHT.ambient, LIGHT.diffuse); light.ambientHex = light.ambient.format(); light.diffuseHex = light.diffuse.format(); light.setPosition(LIGHT.xPos, LIGHT.yPos, LIGHT.zOffset); scene.add(light); LIGHT.proxy = light; LIGHT.pickedup = true; } // Resize canvas function resize(width, height) { renderer.setSize(width, height); FSS.Vector3.set(center, renderer.halfWidth, renderer.halfHeight); createMesh(); } function animate() { render(); requestAnimationFrame(animate); } function render() { renderer.render(scene); } function addEventListeners() { window.addEventListener('resize', onWindowResize); window.addEventListener('mousemove', onMouseMove); } //------------------------------ // Callbacks //------------------------------ function onWindowResize(event) { resize(container.offsetWidth, container.offsetHeight); render(); } function onMouseMove(event) { if(LIGHT.pickedup){ LIGHT.xPos = event.x - renderer.width/2; LIGHT.yPos = renderer.height/2 -event.y; LIGHT.proxy.setPosition(LIGHT.xPos, LIGHT.yPos, LIGHT.proxy.position[2]); } } initialise(); })();