Created
July 2, 2014 20:14
-
-
Save chriscooning/67b9a007609fcebbda42 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (function(){ | |
| //------------------------------ | |
| // Mesh Properties | |
| //------------------------------ | |
| var MESH = { | |
| width: 1.2, | |
| height: 1.2, | |
| depth: 10, | |
| segments: 16, | |
| slices: 8, | |
| xRange: 0.8, | |
| yRange: 0.1, | |
| zRange: 1.0, | |
| ambient: '#555555', | |
| diffuse: '#FFFFFF', | |
| speed: 0.002 | |
| }; | |
| //------------------------------ | |
| // Light Properties | |
| //------------------------------ | |
| var LIGHT = { | |
| count: 2, | |
| xyScalar: 1, | |
| zOffset: 100, | |
| ambient: '#880066', | |
| diffuse: '#FF8800', | |
| speed: 0.001, | |
| gravity: 1200, | |
| dampening: 0.95, | |
| minLimit: 10, | |
| maxLimit: null, | |
| minDistance: 20, | |
| maxDistance: 400, | |
| autopilot: false, | |
| draw: true, | |
| bounds: FSS.Vector3.create(), | |
| step: FSS.Vector3.create( | |
| Math.randomInRange(0.2, 1.0), | |
| Math.randomInRange(0.2, 1.0), | |
| Math.randomInRange(0.2, 1.0) | |
| ) | |
| }; | |
| //------------------------------ | |
| // Render Properties | |
| //------------------------------ | |
| var WEBGL = 'webgl'; | |
| var CANVAS = 'canvas'; | |
| var SVG = 'svg'; | |
| var RENDER = { | |
| renderer: CANVAS | |
| }; | |
| //------------------------------ | |
| // Export Properties | |
| //------------------------------ | |
| var EXPORT = { | |
| width: 2000, | |
| height: 1000, | |
| drawLights: false, | |
| minLightX: 0.4, | |
| maxLightX: 0.6, | |
| minLightY: 0.2, | |
| maxLightY: 0.4, | |
| export: function() { | |
| var l, x, y, light, | |
| depth = MESH.depth, | |
| zOffset = LIGHT.zOffset, | |
| autopilot = LIGHT.autopilot, | |
| scalar = this.width / renderer.width; | |
| LIGHT.autopilot = true; | |
| LIGHT.draw = this.drawLights; | |
| LIGHT.zOffset *= scalar; | |
| MESH.depth *= scalar; | |
| resize(this.width, this.height); | |
| for (l = scene.lights.length - 1; l >= 0; l--) { | |
| light = scene.lights[l]; | |
| x = Math.randomInRange(this.width*this.minLightX, this.width*this.maxLightX); | |
| y = Math.randomInRange(this.height*this.minLightY, this.height*this.maxLightY); | |
| FSS.Vector3.set(light.position, x, this.height-y, this.lightZ); | |
| FSS.Vector3.subtract(light.position, center); | |
| } | |
| update(); | |
| render(); | |
| switch(RENDER.renderer) { | |
| case WEBGL: | |
| window.open(webglRenderer.element.toDataURL(), '_blank'); | |
| break; | |
| case CANVAS: | |
| window.open(canvasRenderer.element.toDataURL(), '_blank'); | |
| break; | |
| case SVG: | |
| var data = encodeURIComponent(output.innerHTML); | |
| var url = "data:image/svg+xml," + data; | |
| window.open(url, '_blank'); | |
| break; | |
| } | |
| LIGHT.draw = true; | |
| LIGHT.autopilot = autopilot; | |
| LIGHT.zOffset = zOffset; | |
| MESH.depth = depth; | |
| resize(container.offsetWidth, container.offsetHeight); | |
| } | |
| }; | |
| //------------------------------ | |
| // UI Properties | |
| //------------------------------ | |
| var UI = { | |
| show: true | |
| }; | |
| //------------------------------ | |
| // Global Properties | |
| //------------------------------ | |
| var now, start = Date.now(); | |
| var center = FSS.Vector3.create(); | |
| var attractor = FSS.Vector3.create(); | |
| var container = document.getElementById('container'); | |
| var controls = document.getElementById('controls'); | |
| var output = document.getElementById('output'); | |
| var ui = document.getElementById('ui'); | |
| var renderer, scene, mesh, geometry, material; | |
| var webglRenderer, canvasRenderer, svgRenderer; | |
| var gui, autopilotController; | |
| //------------------------------ | |
| // Methods | |
| //------------------------------ | |
| function initialise() { | |
| createRenderer(); | |
| createScene(); | |
| createMesh(); | |
| createLights(); | |
| addEventListeners(); | |
| addControls(); | |
| resize(container.offsetWidth, container.offsetHeight); | |
| animate(); | |
| } | |
| function createRenderer() { | |
| webglRenderer = new FSS.WebGLRenderer(); | |
| canvasRenderer = new FSS.CanvasRenderer(); | |
| svgRenderer = new FSS.SVGRenderer(); | |
| setRenderer(RENDER.renderer); | |
| } | |
| function setRenderer(index) { | |
| if (renderer) { | |
| output.removeChild(renderer.element); | |
| } | |
| switch(index) { | |
| case WEBGL: | |
| renderer = webglRenderer; | |
| break; | |
| case CANVAS: | |
| renderer = canvasRenderer; | |
| break; | |
| case SVG: | |
| renderer = svgRenderer; | |
| break; | |
| } | |
| renderer.setSize(container.offsetWidth, container.offsetHeight); | |
| output.appendChild(renderer.element); | |
| } | |
| function createScene() { | |
| scene = new FSS.Scene(); | |
| } | |
| function createMesh() { | |
| scene.remove(mesh); | |
| renderer.clear(); | |
| geometry = new FSS.Plane(MESH.width * renderer.width, MESH.height * renderer.height, MESH.segments, MESH.slices); | |
| material = new FSS.Material(MESH.ambient, MESH.diffuse); | |
| mesh = new FSS.Mesh(geometry, material); | |
| scene.add(mesh); | |
| // Augment vertices for animation | |
| var v, vertex; | |
| for (v = geometry.vertices.length - 1; v >= 0; v--) { | |
| vertex = geometry.vertices[v]; | |
| vertex.anchor = FSS.Vector3.clone(vertex.position); | |
| vertex.step = FSS.Vector3.create( | |
| Math.randomInRange(0.2, 1.0), | |
| Math.randomInRange(0.2, 1.0), | |
| Math.randomInRange(0.2, 1.0) | |
| ); | |
| vertex.time = Math.randomInRange(0, Math.PIM2); | |
| } | |
| } | |
| function createLights() { | |
| var l, light; | |
| for (l = scene.lights.length - 1; l >= 0; l--) { | |
| light = scene.lights[l]; | |
| scene.remove(light); | |
| } | |
| renderer.clear(); | |
| for (l = 0; l < LIGHT.count; l++) { | |
| light = new FSS.Light(LIGHT.ambient, LIGHT.diffuse); | |
| light.ambientHex = light.ambient.format(); | |
| light.diffuseHex = light.diffuse.format(); | |
| scene.add(light); | |
| // Augment light for animation | |
| light.mass = Math.randomInRange(0.5, 1); | |
| light.velocity = FSS.Vector3.create(); | |
| light.acceleration = FSS.Vector3.create(); | |
| light.force = FSS.Vector3.create(); | |
| // Ring SVG Circle | |
| light.ring = document.createElementNS(FSS.SVGNS, 'circle'); | |
| light.ring.setAttributeNS(null, 'stroke', light.ambientHex); | |
| light.ring.setAttributeNS(null, 'stroke-width', '0.5'); | |
| light.ring.setAttributeNS(null, 'fill', 'none'); | |
| light.ring.setAttributeNS(null, 'r', '10'); | |
| // Core SVG Circle | |
| light.core = document.createElementNS(FSS.SVGNS, 'circle'); | |
| light.core.setAttributeNS(null, 'fill', light.diffuseHex); | |
| light.core.setAttributeNS(null, 'r', '4'); | |
| } | |
| } | |
| function resize(width, height) { | |
| renderer.setSize(width, height); | |
| FSS.Vector3.set(center, renderer.halfWidth, renderer.halfHeight); | |
| createMesh(); | |
| } | |
| function animate() { | |
| now = Date.now() - start; | |
| update(); | |
| render(); | |
| requestAnimationFrame(animate); | |
| } | |
| function update() { | |
| var ox, oy, oz, l, light, v, vertex, offset = MESH.depth/2; | |
| // Update Bounds | |
| FSS.Vector3.copy(LIGHT.bounds, center); | |
| FSS.Vector3.multiplyScalar(LIGHT.bounds, LIGHT.xyScalar); | |
| // Update Attractor | |
| FSS.Vector3.setZ(attractor, LIGHT.zOffset); | |
| // Overwrite the Attractor position | |
| if (LIGHT.autopilot) { | |
| ox = Math.sin(LIGHT.step[0] * now * LIGHT.speed); | |
| oy = Math.cos(LIGHT.step[1] * now * LIGHT.speed); | |
| FSS.Vector3.set(attractor, | |
| LIGHT.bounds[0]*ox, | |
| LIGHT.bounds[1]*oy, | |
| LIGHT.zOffset); | |
| } | |
| // Animate Lights | |
| for (l = scene.lights.length - 1; l >= 0; l--) { | |
| light = scene.lights[l]; | |
| // Reset the z position of the light | |
| FSS.Vector3.setZ(light.position, LIGHT.zOffset); | |
| // Calculate the force Luke! | |
| var D = Math.clamp(FSS.Vector3.distanceSquared(light.position, attractor), LIGHT.minDistance, LIGHT.maxDistance); | |
| var F = LIGHT.gravity * light.mass / D; | |
| FSS.Vector3.subtractVectors(light.force, attractor, light.position); | |
| FSS.Vector3.normalise(light.force); | |
| FSS.Vector3.multiplyScalar(light.force, F); | |
| // Update the light position | |
| FSS.Vector3.set(light.acceleration); | |
| FSS.Vector3.add(light.acceleration, light.force); | |
| FSS.Vector3.add(light.velocity, light.acceleration); | |
| FSS.Vector3.multiplyScalar(light.velocity, LIGHT.dampening); | |
| FSS.Vector3.limit(light.velocity, LIGHT.minLimit, LIGHT.maxLimit); | |
| FSS.Vector3.add(light.position, light.velocity); | |
| } | |
| // Animate Vertices | |
| for (v = geometry.vertices.length - 1; v >= 0; v--) { | |
| vertex = geometry.vertices[v]; | |
| ox = Math.sin(vertex.time + vertex.step[0] * now * MESH.speed); | |
| oy = Math.cos(vertex.time + vertex.step[1] * now * MESH.speed); | |
| oz = Math.sin(vertex.time + vertex.step[2] * now * MESH.speed); | |
| FSS.Vector3.set(vertex.position, | |
| MESH.xRange*geometry.segmentWidth*ox, | |
| MESH.yRange*geometry.sliceHeight*oy, | |
| MESH.zRange*offset*oz - offset); | |
| FSS.Vector3.add(vertex.position, vertex.anchor); | |
| } | |
| // Set the Geometry to dirty | |
| geometry.dirty = true; | |
| } | |
| function render() { | |
| renderer.render(scene); | |
| // Draw Lights | |
| if (LIGHT.draw) { | |
| var l, lx, ly, light; | |
| for (l = scene.lights.length - 1; l >= 0; l--) { | |
| light = scene.lights[l]; | |
| lx = light.position[0]; | |
| ly = light.position[1]; | |
| switch(RENDER.renderer) { | |
| case CANVAS: | |
| renderer.context.lineWidth = 0.5; | |
| renderer.context.beginPath(); | |
| renderer.context.arc(lx, ly, 10, 0, Math.PIM2); | |
| renderer.context.strokeStyle = light.ambientHex; | |
| renderer.context.stroke(); | |
| renderer.context.beginPath(); | |
| renderer.context.arc(lx, ly, 4, 0, Math.PIM2); | |
| renderer.context.fillStyle = light.diffuseHex; | |
| renderer.context.fill(); | |
| break; | |
| case SVG: | |
| lx += renderer.halfWidth; | |
| ly = renderer.halfHeight - ly; | |
| light.core.setAttributeNS(null, 'fill', light.diffuseHex); | |
| light.core.setAttributeNS(null, 'cx', lx); | |
| light.core.setAttributeNS(null, 'cy', ly); | |
| renderer.element.appendChild(light.core); | |
| light.ring.setAttributeNS(null, 'stroke', light.ambientHex); | |
| light.ring.setAttributeNS(null, 'cx', lx); | |
| light.ring.setAttributeNS(null, 'cy', ly); | |
| renderer.element.appendChild(light.ring); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| function addEventListeners() { | |
| window.addEventListener('resize', onWindowResize); | |
| container.addEventListener('click', onMouseClick); | |
| container.addEventListener('mousemove', onMouseMove); | |
| } | |
| function addControls() { | |
| var i, l, light, folder, controller; | |
| // Create GUI | |
| gui = new dat.GUI({autoPlace:false}); | |
| controls.appendChild(gui.domElement); | |
| // Create folders | |
| uiFolder = gui.addFolder('UI'); | |
| renderFolder = gui.addFolder('Render'); | |
| meshFolder = gui.addFolder('Mesh'); | |
| lightFolder = gui.addFolder('Light'); | |
| exportFolder = gui.addFolder('Export'); | |
| // Open folders | |
| uiFolder.open(); | |
| renderFolder.open(); | |
| // meshFolder.open(); | |
| lightFolder.open(); | |
| // exportFolder.open(); | |
| // Add UI Controls | |
| controller = uiFolder.add(UI, 'show'); | |
| controller.onChange(function(value) { | |
| ui.className = value ? 'wrapper' : 'wrapper hide'; | |
| }); | |
| // Add Render Controls | |
| controller = renderFolder.add(RENDER, 'renderer', {webgl:WEBGL, canvas:CANVAS, svg:SVG}); | |
| controller.onChange(function(value) { | |
| setRenderer(value); | |
| }); | |
| // Add Mesh Controls | |
| controller = meshFolder.addColor(MESH, 'ambient'); | |
| controller.onChange(function(value) { | |
| for (i = 0, l = scene.meshes.length; i < l; i++) { | |
| scene.meshes[i].material.ambient.set(value); | |
| } | |
| }); | |
| controller = meshFolder.addColor(MESH, 'diffuse'); | |
| controller.onChange(function(value) { | |
| for (i = 0, l = scene.meshes.length; i < l; i++) { | |
| scene.meshes[i].material.diffuse.set(value); | |
| } | |
| }); | |
| controller = meshFolder.add(MESH, 'width', 0.05, 2); | |
| controller.onChange(function(value) { | |
| if (geometry.width !== value * renderer.width) { createMesh(); } | |
| }); | |
| controller = meshFolder.add(MESH, 'height', 0.05, 2); | |
| controller.onChange(function(value) { | |
| if (geometry.height !== value * renderer.height) { createMesh(); } | |
| }); | |
| controller = meshFolder.add(MESH, 'depth', 0, 50); | |
| controller = meshFolder.add(MESH, 'segments', 1, 20); | |
| controller.step(1); | |
| controller.onChange(function(value) { | |
| if (geometry.segments !== value) { createMesh(); } | |
| }); | |
| controller = meshFolder.add(MESH, 'slices', 1, 20); | |
| controller.step(1); | |
| controller.onChange(function(value) { | |
| if (geometry.slices !== value) { createMesh(); } | |
| }); | |
| controller = meshFolder.add(MESH, 'xRange', 0, 1); | |
| controller = meshFolder.add(MESH, 'yRange', 0, 1); | |
| controller = meshFolder.add(MESH, 'speed', 0, 0.01); | |
| // Add Light Controls | |
| autopilotController = lightFolder.add(LIGHT, 'autopilot'); | |
| controller = lightFolder.addColor(LIGHT, 'ambient'); | |
| controller.onChange(function(value) { | |
| for (i = 0, l = scene.lights.length; i < l; i++) { | |
| light = scene.lights[i]; | |
| light.ambient.set(value); | |
| light.ambientHex = light.ambient.format(); | |
| } | |
| }); | |
| controller = lightFolder.addColor(LIGHT, 'diffuse'); | |
| controller.onChange(function(value) { | |
| for (i = 0, l = scene.lights.length; i < l; i++) { | |
| light = scene.lights[i]; | |
| light.diffuse.set(value); | |
| light.diffuseHex = light.diffuse.format(); | |
| } | |
| }); | |
| controller = lightFolder.add(LIGHT, 'count', 0, 5); | |
| controller.step(1); | |
| controller.onChange(function(value) { | |
| if (scene.lights.length !== value) { createLights(); } | |
| }); | |
| controller = lightFolder.add(LIGHT, 'zOffset', 0, 500); | |
| controller.step(1); | |
| // Add Export Controls | |
| controller = exportFolder.add(EXPORT, 'width', 100, 4000); | |
| controller.step(100); | |
| controller = exportFolder.add(EXPORT, 'height', 100, 4000); | |
| controller.step(100); | |
| controller = exportFolder.add(EXPORT, 'drawLights'); | |
| controller = exportFolder.add(EXPORT, 'minLightX', 0, 1); | |
| controller = exportFolder.add(EXPORT, 'maxLightX', 0, 1); | |
| controller = exportFolder.add(EXPORT, 'minLightY', 0, 1); | |
| controller = exportFolder.add(EXPORT, 'maxLightY', 0, 1); | |
| controller = exportFolder.add(EXPORT, 'export'); | |
| } | |
| //------------------------------ | |
| // Callbacks | |
| //------------------------------ | |
| function onMouseClick(event) { | |
| FSS.Vector3.set(attractor, event.x, renderer.height - event.y); | |
| FSS.Vector3.subtract(attractor, center); | |
| LIGHT.autopilot = !LIGHT.autopilot; | |
| autopilotController.updateDisplay(); | |
| } | |
| function onMouseMove(event) { | |
| FSS.Vector3.set(attractor, event.x, renderer.height - event.y); | |
| FSS.Vector3.subtract(attractor, center); | |
| } | |
| function onWindowResize(event) { | |
| resize(container.offsetWidth, container.offsetHeight); | |
| render(); | |
| } | |
| // Let there be light! | |
| initialise(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment