Browse Source

Updated builds.

Mr.doob 10 years ago
parent
commit
e1f89d114e
2 changed files with 6289 additions and 6581 deletions
  1. 6262 6552
      build/three.js
  2. 27 29
      build/three.min.js

+ 6262 - 6552
build/three.js

@@ -15754,6 +15754,88 @@ THREE.Sprite.prototype.clone = function ( object ) {
 
 THREE.Particle = THREE.Sprite;
 
+// File:src/objects/LensFlare.js
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.LensFlare = function ( texture, size, distance, blending, color ) {
+
+	THREE.Object3D.call( this );
+
+	this.lensFlares = [];
+
+	this.positionScreen = new THREE.Vector3();
+	this.customUpdateCallback = undefined;
+
+	if( texture !== undefined ) {
+
+		this.add( texture, size, distance, blending, color );
+
+	}
+
+};
+
+THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype );
+
+
+/*
+ * Add: adds another flare
+ */
+
+THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) {
+
+	if ( size === undefined ) size = - 1;
+	if ( distance === undefined ) distance = 0;
+	if ( opacity === undefined ) opacity = 1;
+	if ( color === undefined ) color = new THREE.Color( 0xffffff );
+	if ( blending === undefined ) blending = THREE.NormalBlending;
+
+	distance = Math.min( distance, Math.max( 0, distance ) );
+
+	this.lensFlares.push( {
+		texture: texture, 			// THREE.Texture
+		size: size, 				// size in pixels (-1 = use texture.width)
+		distance: distance, 		// distance (0-1) from light source (0=at light source)
+		x: 0, y: 0, z: 0,			// screen position (-1 => 1) z = 0 is ontop z = 1 is back
+		scale: 1, 					// scale
+		rotation: 1, 				// rotation
+		opacity: opacity,			// opacity
+		color: color,				// color
+		blending: blending			// blending
+	} );
+
+};
+
+/*
+ * Update lens flares update positions on all flares based on the screen position
+ * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.
+ */
+
+THREE.LensFlare.prototype.updateLensFlares = function () {
+
+	var f, fl = this.lensFlares.length;
+	var flare;
+	var vecX = - this.positionScreen.x * 2;
+	var vecY = - this.positionScreen.y * 2;
+
+	for( f = 0; f < fl; f ++ ) {
+
+		flare = this.lensFlares[ f ];
+
+		flare.x = this.positionScreen.x + vecX * flare.distance;
+		flare.y = this.positionScreen.y + vecY * flare.distance;
+
+		flare.wantedRotation = flare.x * Math.PI * 0.25;
+		flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;
+
+	}
+
+};
+
+
 // File:src/scenes/Scene.js
 
 /**
@@ -17596,6 +17678,9 @@ THREE.WebGLRenderer = function ( parameters ) {
 	var opaqueObjects = [];
 	var transparentObjects = [];
 
+	var sprites = [];
+	var lensFlares = [];
+
 	// public properties
 
 	this.domElement = _canvas;
@@ -17640,11 +17725,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	this.autoScaleCubemaps = true;
 
-	// custom render plugins
-
-	this.renderPluginsPre = [];
-	this.renderPluginsPost = [];
-
 	// info
 
 	this.info = {
@@ -17913,6 +17993,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	}
 
+	// Plugins
+
+	var shadowMapPlugin = new THREE.ShadowMapPlugin( this, lights, _webglObjects, _webglObjectsImmediate );
+
+	var spritePlugin = new THREE.SpritePlugin( this, sprites );
+	var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares );
+
 	// API
 
 	this.getContext = function () {
@@ -18097,24 +18184,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	};
 
-	// Plugins
-
-	this.addPostPlugin = function ( plugin ) {
-
-		plugin.init( this, lights, _webglObjects, _webglObjectsImmediate );
-
-		this.renderPluginsPost.push( plugin );
-
-	};
-
-	this.addPrePlugin = function ( plugin ) {
-
-		plugin.init( this, lights, _webglObjects, _webglObjectsImmediate );
-
-		this.renderPluginsPre.push( plugin );
-
-	};
-
 	// Reset
 
 	this.resetGLState = function () {
@@ -18148,7 +18217,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		_oldDoubleSided = - 1;
 		_oldFlipSided = - 1;
 
-		this.shadowMapPlugin.update( scene, camera );
+		shadowMapPlugin.update( scene, camera );
 
 	};
 
@@ -20955,6 +21024,9 @@ THREE.WebGLRenderer = function ( parameters ) {
 		opaqueObjects.length = 0;
 		transparentObjects.length = 0;
 
+		sprites.length = 0;
+		lensFlares.length = 0;
+
 		projectObject( scene, scene );
 
 		if ( _this.sortObjects === true ) {
@@ -20966,7 +21038,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		// custom render plugins (pre pass)
 
-		renderPlugins( this.renderPluginsPre, scene, camera );
+		shadowMapPlugin.render( scene, camera );
 
 		//
 
@@ -21033,8 +21105,8 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		// custom render plugins (post pass)
 
-		renderPlugins( this.renderPluginsPost, scene, camera );
-
+		spritePlugin.render( scene, camera );
+		lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight );
 
 		// Generate mipmap if we're using any kind of mipmap filtering
 
@@ -21071,32 +21143,44 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			initObject( object, scene );
 
-			var webglObjects = _webglObjects[ object.id ];
+			if ( object instanceof THREE.Sprite ) {
 
-			if ( webglObjects && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) {
+				sprites.push( object );
 
-				updateObject( object, scene );
+			} else if ( object instanceof THREE.LensFlare ) {
 
-				for ( var i = 0, l = webglObjects.length; i < l; i ++ ) {
+				lensFlares.push( object );
 
-					var webglObject = webglObjects[i];
+			} else {
 
-					unrollBufferMaterial( webglObject );
+				var webglObjects = _webglObjects[ object.id ];
 
-					webglObject.render = true;
+				if ( webglObjects && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) {
 
-					if ( _this.sortObjects === true ) {
+					updateObject( object, scene );
 
-						if ( object.renderDepth !== null ) {
+					for ( var i = 0, l = webglObjects.length; i < l; i ++ ) {
 
-							webglObject.z = object.renderDepth;
+						var webglObject = webglObjects[i];
 
-						} else {
+						unrollBufferMaterial( webglObject );
+
+						webglObject.render = true;
+
+						if ( _this.sortObjects === true ) {
+
+							if ( object.renderDepth !== null ) {
+
+								webglObject.z = object.renderDepth;
 
-							_vector3.setFromMatrixPosition( object.matrixWorld );
-							_vector3.applyProjection( _projScreenMatrix );
+							} else {
+
+								_vector3.setFromMatrixPosition( object.matrixWorld );
+								_vector3.applyProjection( _projScreenMatrix );
 
-							webglObject.z = _vector3.z;
+								webglObject.z = _vector3.z;
+
+							}
 
 						}
 
@@ -21116,18 +21200,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	}
 
-	function renderPlugins( plugins, scene, camera ) {
-
-		if ( plugins.length === 0 ) return;
-
-		for ( var i = 0, il = plugins.length; i < il; i ++ ) {
-
-			plugins[ i ].render( scene, camera, _currentWidth, _currentHeight );
-
-		}
-
-	}
-
 	function renderObjects( renderList, camera, lights, fog, useBlending, overrideMaterial ) {
 
 		var material;
@@ -21651,7 +21723,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	// Objects updates - custom attributes check
 
@@ -21665,7 +21737,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		return false;
 
-	};
+	}
 
 	function clearCustomAttributes( material ) {
 
@@ -21675,7 +21747,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	// Objects removal
 
@@ -21699,7 +21771,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		delete object.__webglActive;
 
-	};
+	}
 
 	function removeInstances( objlist, object ) {
 
@@ -21713,16 +21785,10 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	// Materials
 
-	this.initMaterial = function () {
-
-		console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' );
-
-	};
-
 	function initMaterial( material, lights, fog, object ) {
 
 		material.addEventListener( 'dispose', onMaterialDispose );
@@ -21959,7 +22025,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	function setProgram( camera, lights, fog, material, object ) {
 
@@ -22204,7 +22270,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		return program;
 
-	};
+	}
 
 	// Uniforms (refresh uniforms objects)
 
@@ -22299,14 +22365,14 @@ THREE.WebGLRenderer = function ( parameters ) {
 		uniforms.combine.value = material.combine;
 		uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping;
 
-	};
+	}
 
 	function refreshUniformsLine ( uniforms, material ) {
 
 		uniforms.diffuse.value = material.color;
 		uniforms.opacity.value = material.opacity;
 
-	};
+	}
 
 	function refreshUniformsDash ( uniforms, material ) {
 
@@ -22314,7 +22380,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		uniforms.totalSize.value = material.dashSize + material.gapSize;
 		uniforms.scale.value = material.scale;
 
-	};
+	}
 
 	function refreshUniformsParticle ( uniforms, material ) {
 
@@ -22325,7 +22391,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		uniforms.map.value = material.map;
 
-	};
+	}
 
 	function refreshUniformsFog ( uniforms, fog ) {
 
@@ -22342,7 +22408,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	function refreshUniformsPhong ( uniforms, material ) {
 
@@ -22368,7 +22434,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	function refreshUniformsLambert ( uniforms, material ) {
 
@@ -22390,7 +22456,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	function refreshUniformsLights ( uniforms, lights ) {
 
@@ -22414,7 +22480,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors;
 		uniforms.hemisphereLightDirection.value = lights.hemi.positions;
 
-	};
+	}
 
 	// If uniforms are marked as clean, they don't need to be loaded to the GPU.
 
@@ -22440,7 +22506,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		uniforms.hemisphereLightGroundColor.needsUpdate = boolean;
 		uniforms.hemisphereLightDirection.needsUpdate = boolean;
 
-	};
+	}
 
 	function refreshUniformsShadow ( uniforms, lights ) {
 
@@ -22472,7 +22538,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	// Uniforms (load to GPU)
 
@@ -22486,7 +22552,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	function getTextureUnit() {
 
@@ -22502,7 +22568,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		return textureUnit;
 
-	};
+	}
 
 	function loadUniformsGeneric ( uniforms ) {
 
@@ -22838,14 +22904,14 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	function setupMatrices ( object, camera ) {
 
 		object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
 		object._normalMatrix.getNormalMatrix( object._modelViewMatrix );
 
-	};
+	}
 
 	//
 
@@ -22855,7 +22921,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		array[ offset + 1 ] = color.g * color.g * intensitySq;
 		array[ offset + 2 ] = color.b * color.b * intensitySq;
 
-	};
+	}
 
 	function setColorLinear( array, offset, color, intensity ) {
 
@@ -22863,7 +22929,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		array[ offset + 1 ] = color.g * intensity;
 		array[ offset + 2 ] = color.b * intensity;
 
-	};
+	}
 
 	function setupLights ( lights ) {
 
@@ -23089,7 +23155,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 		zlights.ambient[ 1 ] = g;
 		zlights.ambient[ 2 ] = b;
 
-	};
+	}
 
 	// GL state setting
 
@@ -23211,7 +23277,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	function setPolygonOffset ( polygonoffset, factor, units ) {
 
@@ -23240,7 +23306,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) {
 
@@ -23352,7 +23418,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-	};
+	}
 
 	this.uploadTexture = function ( texture ) {
 
@@ -24020,13 +24086,25 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	}
 
-	// default plugins (order is important)
+	// DEPRECATED
+	
+	this.initMaterial = function () {
+
+		console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' );
+
+	};
+
+	this.addPrePlugin = function () {
+
+		console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' );
 
-	this.shadowMapPlugin = new THREE.ShadowMapPlugin( this );
-	this.addPrePlugin( this.shadowMapPlugin );
+	};
+
+	this.addPostPlugin = function () {
 
-	this.addPostPlugin( new THREE.SpritePlugin( this ) );
-	this.addPostPlugin( new THREE.LensFlarePlugin( this ) );
+		console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' );
+
+	};
 
 };
 
@@ -24632,10384 +24710,10016 @@ THREE.WebGLShader = ( function () {
 
 } )();
 
-// File:src/extras/GeometryUtils.js
+// File:src/renderers/webgl/plugins/LensFlarePlugin.js
 
 /**
- * @author mrdoob / http://mrdoob.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
  */
 
-THREE.GeometryUtils = {
+THREE.LensFlarePlugin = function ( renderer, flares ) {
 
-	merge: function ( geometry1, geometry2, materialIndexOffset ) {
+	var gl = renderer.context;
 
-		console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' );
+	var vertexBuffer, elementBuffer;
+	var program, attributes, uniforms;
+	var hasVertexTexture;
 
-		var matrix;
+	var tempTexture, occlusionTexture;
 
-		if ( geometry2 instanceof THREE.Mesh ) {
+	var init = function () {
 
-			geometry2.matrixAutoUpdate && geometry2.updateMatrix();
+		var vertices = new Float32Array( [
+			-1, -1,  0, 0,
+			 1, -1,  1, 0,
+			 1,  1,  1, 1,
+			-1,  1,  0, 1
+		] );
 
-			matrix = geometry2.matrix;
-			geometry2 = geometry2.geometry;
+		var faces = new Uint16Array( [
+			0, 1, 2,
+			0, 2, 3
+		] );
 
-		}
+		// buffers
 
-		geometry1.merge( geometry2, matrix, materialIndexOffset );
+		vertexBuffer     = gl.createBuffer();
+		elementBuffer    = gl.createBuffer();
 
-	},
+		gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+		gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
 
-	center: function ( geometry ) {
+		gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+		gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
 
-		console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' );
-		return geometry.center();
+		// textures
 
-	}
+		tempTexture      = gl.createTexture();
+		occlusionTexture = gl.createTexture();
 
-};
+		gl.bindTexture( gl.TEXTURE_2D, tempTexture );
+		gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null );
+		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
+		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
+		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
+		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
 
-// File:src/extras/ImageUtils.js
+		gl.bindTexture( gl.TEXTURE_2D, occlusionTexture );
+		gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null );
+		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
+		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
+		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
+		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
 
-/**
- * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com/
- * @author Daosheng Mu / https://github.com/DaoshengMu/
- */
+		hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0;
 
-THREE.ImageUtils = {
+		var shader;
 
-	crossOrigin: undefined,
+		if ( hasVertexTexture ) {
 
-	loadTexture: function ( url, mapping, onLoad, onError ) {
+			shader = {
 
-		var loader = new THREE.ImageLoader();
-		loader.crossOrigin = this.crossOrigin;
+				vertexShader: [
 
-		var texture = new THREE.Texture( undefined, mapping );
+					"uniform lowp int renderType;",
 
-		loader.load( url, function ( image ) {
+					"uniform vec3 screenPosition;",
+					"uniform vec2 scale;",
+					"uniform float rotation;",
 
-			texture.image = image;
-			texture.needsUpdate = true;
+					"uniform sampler2D occlusionMap;",
 
-			if ( onLoad ) onLoad( texture );
+					"attribute vec2 position;",
+					"attribute vec2 uv;",
 
-		}, undefined, function ( event ) {
+					"varying vec2 vUV;",
+					"varying float vVisibility;",
 
-			if ( onError ) onError( event );
+					"void main() {",
 
-		} );
+						"vUV = uv;",
 
-		texture.sourceFile = url;
+						"vec2 pos = position;",
 
-		return texture;
+						"if( renderType == 2 ) {",
 
-	},
+							"vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );",
+							"visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );",
+							"visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );",
+							"visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );",
+							"visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );",
+							"visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );",
+							"visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );",
+							"visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );",
+							"visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );",
 
-	loadTextureCube: function ( array, mapping, onLoad, onError ) {
+							"vVisibility =        visibility.r / 9.0;",
+							"vVisibility *= 1.0 - visibility.g / 9.0;",
+							"vVisibility *=       visibility.b / 9.0;",
+							"vVisibility *= 1.0 - visibility.a / 9.0;",
 
-		var images = [];
+							"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
+							"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
 
-		var loader = new THREE.ImageLoader();
-		loader.crossOrigin = this.crossOrigin;
+						"}",
 
-		var texture = new THREE.CubeTexture( images, mapping );
+						"gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
 
-		// no flipping needed for cube textures
+					"}"
 
-		texture.flipY = false;
+				].join( "\n" ),
 
-		var loaded = 0;
+				fragmentShader: [
 
-		var loadTexture = function ( i ) {
+					"uniform lowp int renderType;",
 
-			loader.load( array[ i ], function ( image ) {
+					"uniform sampler2D map;",
+					"uniform float opacity;",
+					"uniform vec3 color;",
 
-				texture.images[ i ] = image;
+					"varying vec2 vUV;",
+					"varying float vVisibility;",
 
-				loaded += 1;
+					"void main() {",
 
-				if ( loaded === 6 ) {
+						// pink square
 
-					texture.needsUpdate = true;
+						"if( renderType == 0 ) {",
 
-					if ( onLoad ) onLoad( texture );
+							"gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
 
-				}
+						// restore
 
-			} );
+						"} else if( renderType == 1 ) {",
 
-		}
+							"gl_FragColor = texture2D( map, vUV );",
 
-		for ( var i = 0, il = array.length; i < il; ++ i ) {
+						// flare
 
-			loadTexture( i );
+						"} else {",
 
-		}
+							"vec4 texture = texture2D( map, vUV );",
+							"texture.a *= opacity * vVisibility;",
+							"gl_FragColor = texture;",
+							"gl_FragColor.rgb *= color;",
 
-		return texture;
+						"}",
 
-	},
+					"}"
 
-	loadCompressedTexture: function () {
+				].join( "\n" )
 
-		console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' )
+			};
 
-	},
+		} else {
 
-	loadCompressedTextureCube: function () {
+			shader = {
 
-		console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' )
+				vertexShader: [
 
-	},
+					"uniform lowp int renderType;",
 
-	getNormalMap: function ( image, depth ) {
+					"uniform vec3 screenPosition;",
+					"uniform vec2 scale;",
+					"uniform float rotation;",
 
-		// Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/
+					"attribute vec2 position;",
+					"attribute vec2 uv;",
 
-		var cross = function ( a, b ) {
+					"varying vec2 vUV;",
 
-			return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ];
+					"void main() {",
 
-		}
+						"vUV = uv;",
 
-		var subtract = function ( a, b ) {
+						"vec2 pos = position;",
 
-			return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ];
+						"if( renderType == 2 ) {",
 
-		}
+							"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
+							"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
 
-		var normalize = function ( a ) {
+						"}",
 
-			var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] );
-			return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ];
+						"gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
 
-		}
+					"}"
 
-		depth = depth | 1;
+				].join( "\n" ),
 
-		var width = image.width;
-		var height = image.height;
+				fragmentShader: [
 
-		var canvas = document.createElement( 'canvas' );
-		canvas.width = width;
-		canvas.height = height;
+					"precision mediump float;",
 
-		var context = canvas.getContext( '2d' );
-		context.drawImage( image, 0, 0 );
+					"uniform lowp int renderType;",
 
-		var data = context.getImageData( 0, 0, width, height ).data;
-		var imageData = context.createImageData( width, height );
-		var output = imageData.data;
+					"uniform sampler2D map;",
+					"uniform sampler2D occlusionMap;",
+					"uniform float opacity;",
+					"uniform vec3 color;",
 
-		for ( var x = 0; x < width; x ++ ) {
+					"varying vec2 vUV;",
 
-			for ( var y = 0; y < height; y ++ ) {
+					"void main() {",
 
-				var ly = y - 1 < 0 ? 0 : y - 1;
-				var uy = y + 1 > height - 1 ? height - 1 : y + 1;
-				var lx = x - 1 < 0 ? 0 : x - 1;
-				var ux = x + 1 > width - 1 ? width - 1 : x + 1;
+						// pink square
 
-				var points = [];
-				var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ];
-				points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] );
-				points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] );
-				points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] );
-				points.push( [  1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] );
-				points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] );
-				points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] );
-				points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] );
-				points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] );
+						"if( renderType == 0 ) {",
 
-				var normals = [];
-				var num_points = points.length;
+							"gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );",
 
-				for ( var i = 0; i < num_points; i ++ ) {
+						// restore
 
-					var v1 = points[ i ];
-					var v2 = points[ ( i + 1 ) % num_points ];
-					v1 = subtract( v1, origin );
-					v2 = subtract( v2, origin );
-					normals.push( normalize( cross( v1, v2 ) ) );
+						"} else if( renderType == 1 ) {",
 
-				}
+							"gl_FragColor = texture2D( map, vUV );",
 
-				var normal = [ 0, 0, 0 ];
+						// flare
 
-				for ( var i = 0; i < normals.length; i ++ ) {
+						"} else {",
 
-					normal[ 0 ] += normals[ i ][ 0 ];
-					normal[ 1 ] += normals[ i ][ 1 ];
-					normal[ 2 ] += normals[ i ][ 2 ];
+							"float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;",
+							"visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;",
+							"visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;",
+							"visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;",
+							"visibility = ( 1.0 - visibility / 4.0 );",
 
-				}
+							"vec4 texture = texture2D( map, vUV );",
+							"texture.a *= opacity * visibility;",
+							"gl_FragColor = texture;",
+							"gl_FragColor.rgb *= color;",
 
-				normal[ 0 ] /= normals.length;
-				normal[ 1 ] /= normals.length;
-				normal[ 2 ] /= normals.length;
+						"}",
 
-				var idx = ( y * width + x ) * 4;
+					"}"
 
-				output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0;
-				output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0;
-				output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0;
-				output[ idx + 3 ] = 255;
+				].join( "\n" )
 
-			}
+			};
 
 		}
 
-		context.putImageData( imageData, 0, 0 );
-
-		return canvas;
+		program = createProgram( shader );
 
-	},
+		attributes = {
+			vertex: gl.getAttribLocation ( program, "position" ),
+			uv:     gl.getAttribLocation ( program, "uv" )
+		}
 
-	generateDataTexture: function ( width, height, color ) {
+		uniforms = {
+			renderType:     gl.getUniformLocation( program, "renderType" ),
+			map:            gl.getUniformLocation( program, "map" ),
+			occlusionMap:   gl.getUniformLocation( program, "occlusionMap" ),
+			opacity:        gl.getUniformLocation( program, "opacity" ),
+			color:          gl.getUniformLocation( program, "color" ),
+			scale:          gl.getUniformLocation( program, "scale" ),
+			rotation:       gl.getUniformLocation( program, "rotation" ),
+			screenPosition: gl.getUniformLocation( program, "screenPosition" )
+		};
 
-		var size = width * height;
-		var data = new Uint8Array( 3 * size );
+	};
 
-		var r = Math.floor( color.r * 255 );
-		var g = Math.floor( color.g * 255 );
-		var b = Math.floor( color.b * 255 );
+	/*
+	 * Render lens flares
+	 * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
+	 *         reads these back and calculates occlusion.
+	 */
 
-		for ( var i = 0; i < size; i ++ ) {
+	this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
 
-			data[ i * 3 ] 	   = r;
-			data[ i * 3 + 1 ] = g;
-			data[ i * 3 + 2 ] = b;
+		if ( flares.length === 0 ) return;
 
-		}
+		var tempPosition = new THREE.Vector3();
 
-		var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat );
-		texture.needsUpdate = true;
+		var invAspect = viewportHeight / viewportWidth,
+			halfViewportWidth = viewportWidth * 0.5,
+			halfViewportHeight = viewportHeight * 0.5;
 
-		return texture;
+		var size = 16 / viewportHeight,
+			scale = new THREE.Vector2( size * invAspect, size );
 
-	}
+		var screenPosition = new THREE.Vector3( 1, 1, 0 ),
+			screenPositionPixels = new THREE.Vector2( 1, 1 );
 
-};
+		if ( program === undefined ) {
 
-// File:src/extras/SceneUtils.js
+			init();
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
+		}
 
-THREE.SceneUtils = {
+		gl.useProgram( program );
 
-	createMultiMaterialObject: function ( geometry, materials ) {
+		gl.enableVertexAttribArray( attributes.vertex );
+		gl.enableVertexAttribArray( attributes.uv );
 
-		var group = new THREE.Object3D();
+		// loop through all lens flares to update their occlusion and positions
+		// setup gl and common used attribs/unforms
 
-		for ( var i = 0, l = materials.length; i < l; i ++ ) {
+		gl.uniform1i( uniforms.occlusionMap, 0 );
+		gl.uniform1i( uniforms.map, 1 );
 
-			group.add( new THREE.Mesh( geometry, materials[ i ] ) );
+		gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+		gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 );
+		gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
 
-		}
+		gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
 
-		return group;
+		gl.disable( gl.CULL_FACE );
+		gl.depthMask( false );
 
-	},
+		for ( var i = 0, l = flares.length; i < l; i ++ ) {
 
-	detach: function ( child, parent, scene ) {
+			size = 16 / viewportHeight;
+			scale.set( size * invAspect, size );
 
-		child.applyMatrix( parent.matrixWorld );
-		parent.remove( child );
-		scene.add( child );
+			// calc object screen position
 
-	},
+			var flare = flares[ i ];
+			
+			tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] );
 
-	attach: function ( child, scene, parent ) {
+			tempPosition.applyMatrix4( camera.matrixWorldInverse );
+			tempPosition.applyProjection( camera.projectionMatrix );
 
-		var matrixWorldInverse = new THREE.Matrix4();
-		matrixWorldInverse.getInverse( parent.matrixWorld );
-		child.applyMatrix( matrixWorldInverse );
+			// setup arrays for gl programs
 
-		scene.remove( child );
-		parent.add( child );
+			screenPosition.copy( tempPosition )
 
-	}
+			screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth;
+			screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight;
 
-};
+			// screen cull
 
-// File:src/extras/FontUtils.js
+			if ( hasVertexTexture || (
+				screenPositionPixels.x > 0 &&
+				screenPositionPixels.x < viewportWidth &&
+				screenPositionPixels.y > 0 &&
+				screenPositionPixels.y < viewportHeight ) ) {
 
-/**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- * @author alteredq / http://alteredqualia.com/
- *
- * For Text operations in three.js (See TextGeometry)
- *
- * It uses techniques used in:
- *
- * 	typeface.js and canvastext
- * 		For converting fonts and rendering with javascript
- *		http://typeface.neocracy.org
- *
- *	Triangulation ported from AS3
- *		Simple Polygon Triangulation
- *		http://actionsnippet.com/?p=1462
- *
- * 	A Method to triangulate shapes with holes
- *		http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/
- *
- */
+				// save current RGB to temp texture
 
-THREE.FontUtils = {
+				gl.activeTexture( gl.TEXTURE1 );
+				gl.bindTexture( gl.TEXTURE_2D, tempTexture );
+				gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
 
-	faces: {},
 
-	// Just for now. face[weight][style]
+				// render pink quad
 
-	face: 'helvetiker',
-	weight: 'normal',
-	style: 'normal',
-	size: 150,
-	divisions: 10,
+				gl.uniform1i( uniforms.renderType, 0 );
+				gl.uniform2f( uniforms.scale, scale.x, scale.y );
+				gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
 
-	getFace: function () {
+				gl.disable( gl.BLEND );
+				gl.enable( gl.DEPTH_TEST );
 
-		try {
+				gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
 
-			return this.faces[ this.face ][ this.weight ][ this.style ];
 
-		} catch (e) {
+				// copy result to occlusionMap
 
-			throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing."
+				gl.activeTexture( gl.TEXTURE0 );
+				gl.bindTexture( gl.TEXTURE_2D, occlusionTexture );
+				gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
 
-		};
 
-	},
+				// restore graphics
 
-	loadFace: function ( data ) {
+				gl.uniform1i( uniforms.renderType, 1 );
+				gl.disable( gl.DEPTH_TEST );
 
-		var family = data.familyName.toLowerCase();
+				gl.activeTexture( gl.TEXTURE1 );
+				gl.bindTexture( gl.TEXTURE_2D, tempTexture );
+				gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
 
-		var ThreeFont = this;
 
-		ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
+				// update object positions
 
-		ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
-		ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
+				flare.positionScreen.copy( screenPosition )
 
-		var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
+				if ( flare.customUpdateCallback ) {
 
-		return data;
+					flare.customUpdateCallback( flare );
 
-	},
+				} else {
 
-	drawText: function ( text ) {
+					flare.updateLensFlares();
 
-		var characterPts = [], allPts = [];
+				}
 
-		// RenderText
+				// render flares
 
-		var i, p,
-			face = this.getFace(),
-			scale = this.size / face.resolution,
-			offset = 0,
-			chars = String( text ).split( '' ),
-			length = chars.length;
+				gl.uniform1i( uniforms.renderType, 2 );
+				gl.enable( gl.BLEND );
 
-		var fontPaths = [];
+				for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {
 
-		for ( i = 0; i < length; i ++ ) {
+					var sprite = flare.lensFlares[ j ];
 
-			var path = new THREE.Path();
+					if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) {
 
-			var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path );
-			offset += ret.offset;
+						screenPosition.x = sprite.x;
+						screenPosition.y = sprite.y;
+						screenPosition.z = sprite.z;
 
-			fontPaths.push( ret.path );
+						size = sprite.size * sprite.scale / viewportHeight;
 
-		}
+						scale.x = size * invAspect;
+						scale.y = size;
 
-		// get the width
+						gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
+						gl.uniform2f( uniforms.scale, scale.x, scale.y );
+						gl.uniform1f( uniforms.rotation, sprite.rotation );
 
-		var width = offset / 2;
-		//
-		// for ( p = 0; p < allPts.length; p++ ) {
-		//
-		// 	allPts[ p ].x -= width;
-		//
-		// }
+						gl.uniform1f( uniforms.opacity, sprite.opacity );
+						gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );
 
-		//var extract = this.extractPoints( allPts, characterPts );
-		//extract.contour = allPts;
+						renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );
+						renderer.setTexture( sprite.texture, 1 );
 
-		//extract.paths = fontPaths;
-		//extract.offset = width;
+						gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
 
-		return { paths: fontPaths, offset: width };
+					}
 
-	},
+				}
 
+			}
 
+		}
 
+		// restore gl
 
-	extractGlyphPoints: function ( c, face, scale, offset, path ) {
+		gl.enable( gl.CULL_FACE );
+		gl.enable( gl.DEPTH_TEST );
+		gl.depthMask( true );
 
-		var pts = [];
+		renderer.resetGLState();
 
-		var i, i2, divisions,
-			outline, action, length,
-			scaleX, scaleY,
-			x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2,
-			laste,
-			glyph = face.glyphs[ c ] || face.glyphs[ '?' ];
+	};
 
-		if ( ! glyph ) return;
+	function createProgram ( shader ) {
 
-		if ( glyph.o ) {
+		var program = gl.createProgram();
 
-			outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
-			length = outline.length;
+		var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
+		var vertexShader = gl.createShader( gl.VERTEX_SHADER );
 
-			scaleX = scale;
-			scaleY = scale;
+		var prefix = "precision " + renderer.getPrecision() + " float;\n";
 
-			for ( i = 0; i < length; ) {
+		gl.shaderSource( fragmentShader, prefix + shader.fragmentShader );
+		gl.shaderSource( vertexShader, prefix + shader.vertexShader );
 
-				action = outline[ i ++ ];
+		gl.compileShader( fragmentShader );
+		gl.compileShader( vertexShader );
 
-				//console.log( action );
+		gl.attachShader( program, fragmentShader );
+		gl.attachShader( program, vertexShader );
 
-				switch ( action ) {
+		gl.linkProgram( program );
 
-				case 'm':
+		return program;
 
-					// Move To
+	}
 
-					x = outline[ i ++ ] * scaleX + offset;
-					y = outline[ i ++ ] * scaleY;
+};
 
-					path.moveTo( x, y );
-					break;
+// File:src/renderers/webgl/plugins/ShadowMapPlugin.js
 
-				case 'l':
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-					// Line To
+THREE.ShadowMapPlugin = function ( _renderer, _lights, _webglObjects, _webglObjectsImmediate ) {
 
-					x = outline[ i ++ ] * scaleX + offset;
-					y = outline[ i ++ ] * scaleY;
-					path.lineTo( x,y );
-					break;
+	var _gl = _renderer.context;
 
-				case 'q':
+	var _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
 
-					// QuadraticCurveTo
+	_frustum = new THREE.Frustum(),
+	_projScreenMatrix = new THREE.Matrix4(),
 
-					cpx  = outline[ i ++ ] * scaleX + offset;
-					cpy  = outline[ i ++ ] * scaleY;
-					cpx1 = outline[ i ++ ] * scaleX + offset;
-					cpy1 = outline[ i ++ ] * scaleY;
+	_min = new THREE.Vector3(),
+	_max = new THREE.Vector3(),
 
-					path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
+	_matrixPosition = new THREE.Vector3(),
+	
+	_renderList = [];
 
-					laste = pts[ pts.length - 1 ];
+	// init
 
-					if ( laste ) {
+	var depthShader = THREE.ShaderLib[ "depthRGBA" ];
+	var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
 
-						cpx0 = laste.x;
-						cpy0 = laste.y;
+	_depthMaterial = new THREE.ShaderMaterial( {
+		uniforms: depthUniforms,
+		vertexShader: depthShader.vertexShader,
+		fragmentShader: depthShader.fragmentShader
+	 } );
 
-						for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
+	_depthMaterialMorph = new THREE.ShaderMaterial( {
+		uniforms: depthUniforms,
+		vertexShader: depthShader.vertexShader,
+		fragmentShader: depthShader.fragmentShader,
+		morphTargets: true
+	} );
 
-							var t = i2 / divisions;
-							var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
-							var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
-					  }
+	_depthMaterialSkin = new THREE.ShaderMaterial( {
+		uniforms: depthUniforms,
+		vertexShader: depthShader.vertexShader,
+		fragmentShader: depthShader.fragmentShader,
+		skinning: true
+	} );
 
-				  }
+	_depthMaterialMorphSkin = new THREE.ShaderMaterial( {
+		uniforms: depthUniforms,
+		vertexShader: depthShader.vertexShader,
+		fragmentShader: depthShader.fragmentShader,
+		morphTargets: true,
+		skinning: true
+	} );
 
-				  break;
+	_depthMaterial._shadowPass = true;
+	_depthMaterialMorph._shadowPass = true;
+	_depthMaterialSkin._shadowPass = true;
+	_depthMaterialMorphSkin._shadowPass = true;
 
-				case 'b':
+	this.render = function ( scene, camera ) {
 
-					// Cubic Bezier Curve
+		if ( _renderer.shadowMapEnabled === false || _renderer.shadowMapAutoUpdate === false ) return;
 
-					cpx  = outline[ i ++ ] *  scaleX + offset;
-					cpy  = outline[ i ++ ] *  scaleY;
-					cpx1 = outline[ i ++ ] *  scaleX + offset;
-					cpy1 = outline[ i ++ ] *  scaleY;
-					cpx2 = outline[ i ++ ] *  scaleX + offset;
-					cpy2 = outline[ i ++ ] *  scaleY;
+		this.update( scene, camera );
 
-					path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
+	};
 
-					laste = pts[ pts.length - 1 ];
+	this.update = function ( scene, camera ) {
 
-					if ( laste ) {
+		var i, il, j, jl, n,
 
-						cpx0 = laste.x;
-						cpy0 = laste.y;
+		shadowMap, shadowMatrix, shadowCamera,
+		program, buffer, material,
+		webglObject, object, light,
 
-						for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
+		lights = [],
+		k = 0,
 
-							var t = i2 / divisions;
-							var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
-							var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
+		fog = null;
 
-						}
+		// set GL state for depth map
 
-					}
+		_gl.clearColor( 1, 1, 1, 1 );
+		_gl.disable( _gl.BLEND );
 
-					break;
+		_gl.enable( _gl.CULL_FACE );
+		_gl.frontFace( _gl.CCW );
 
-				}
+		if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
 
-			}
-		}
+			_gl.cullFace( _gl.FRONT );
 
+		} else {
 
+			_gl.cullFace( _gl.BACK );
 
-		return { offset: glyph.ha * scale, path:path };
-	}
+		}
 
-};
+		_renderer.setDepthTest( true );
 
+		// preprocess lights
+		// 	- skip lights that are not casting shadows
+		//	- create virtual lights for cascaded shadow maps
 
-THREE.FontUtils.generateShapes = function ( text, parameters ) {
+		for ( i = 0, il = _lights.length; i < il; i ++ ) {
 
-	// Parameters 
+			light = _lights[ i ];
 
-	parameters = parameters || {};
+			if ( ! light.castShadow ) continue;
 
-	var size = parameters.size !== undefined ? parameters.size : 100;
-	var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4;
+			if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) {
 
-	var font = parameters.font !== undefined ? parameters.font : 'helvetiker';
-	var weight = parameters.weight !== undefined ? parameters.weight : 'normal';
-	var style = parameters.style !== undefined ? parameters.style : 'normal';
+				for ( n = 0; n < light.shadowCascadeCount; n ++ ) {
 
-	THREE.FontUtils.size = size;
-	THREE.FontUtils.divisions = curveSegments;
+					var virtualLight;
 
-	THREE.FontUtils.face = font;
-	THREE.FontUtils.weight = weight;
-	THREE.FontUtils.style = style;
+					if ( ! light.shadowCascadeArray[ n ] ) {
 
-	// Get a Font data json object
+						virtualLight = createVirtualLight( light, n );
+						virtualLight.originalCamera = camera;
 
-	var data = THREE.FontUtils.drawText( text );
+						var gyro = new THREE.Gyroscope();
+						gyro.position.copy( light.shadowCascadeOffset );
 
-	var paths = data.paths;
-	var shapes = [];
+						gyro.add( virtualLight );
+						gyro.add( virtualLight.target );
 
-	for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
+						camera.add( gyro );
 
-		Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
+						light.shadowCascadeArray[ n ] = virtualLight;
 
-	}
+						console.log( "Created virtualLight", virtualLight );
 
-	return shapes;
+					} else {
 
-};
+						virtualLight = light.shadowCascadeArray[ n ];
 
+					}
 
-/**
- * This code is a quick port of code written in C++ which was submitted to
- * flipcode.com by John W. Ratcliff  // July 22, 2000
- * See original code and more information here:
- * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
- *
- * ported to actionscript by Zevan Rosser
- * www.actionsnippet.com
- *
- * ported to javascript by Joshua Koo
- * http://www.lab4games.net/zz85/blog
- *
- */
+					updateVirtualLight( light, n );
 
+					lights[ k ] = virtualLight;
+					k ++;
 
-( function ( namespace ) {
+				}
 
-	var EPSILON = 0.0000000001;
+			} else {
 
-	// takes in an contour array and returns
+				lights[ k ] = light;
+				k ++;
 
-	var process = function ( contour, indices ) {
+			}
 
-		var n = contour.length;
+		}
 
-		if ( n < 3 ) return null;
+		// render depth map
 
-		var result = [],
-			verts = [],
-			vertIndices = [];
+		for ( i = 0, il = lights.length; i < il; i ++ ) {
 
-		/* we want a counter-clockwise polygon in verts */
+			light = lights[ i ];
 
-		var u, v, w;
+			if ( ! light.shadowMap ) {
 
-		if ( area( contour ) > 0.0 ) {
+				var shadowFilter = THREE.LinearFilter;
 
-			for ( v = 0; v < n; v ++ ) verts[ v ] = v;
+				if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) {
 
-		} else {
+					shadowFilter = THREE.NearestFilter;
 
-			for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v;
+				}
 
-		}
+				var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat };
 
-		var nv = n;
+				light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars );
+				light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight );
 
-		/*  remove nv - 2 vertices, creating 1 triangle every time */
+				light.shadowMatrix = new THREE.Matrix4();
 
-		var count = 2 * nv;   /* error detection */
+			}
 
-		for ( v = nv - 1; nv > 2; ) {
+			if ( ! light.shadowCamera ) {
 
-			/* if we loop, it is probably a non-simple polygon */
+				if ( light instanceof THREE.SpotLight ) {
 
-			if ( ( count -- ) <= 0 ) {
+					light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar );
 
-				//** Triangulate: ERROR - probable bad polygon!
+				} else if ( light instanceof THREE.DirectionalLight ) {
 
-				//throw ( "Warning, unable to triangulate polygon!" );
-				//return null;
-				// Sometimes warning is fine, especially polygons are triangulated in reverse.
-				console.log( 'Warning, unable to triangulate polygon!' );
+					light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar );
 
-				if ( indices ) return vertIndices;
-				return result;
+				} else {
 
-			}
+					console.error( "Unsupported light type for shadow" );
+					continue;
 
-			/* three consecutive vertices in current polygon, <u,v,w> */
+				}
 
-			u = v; 	 	if ( nv <= u ) u = 0;     /* previous */
-			v = u + 1;  if ( nv <= v ) v = 0;     /* new v    */
-			w = v + 1;  if ( nv <= w ) w = 0;     /* next     */
+				scene.add( light.shadowCamera );
 
-			if ( snip( contour, u, v, w, nv, verts ) ) {
+				if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
 
-				var a, b, c, s, t;
+			}
 
-				/* true names of the vertices */
+			if ( light.shadowCameraVisible && ! light.cameraHelper ) {
 
-				a = verts[ u ];
-				b = verts[ v ];
-				c = verts[ w ];
+				light.cameraHelper = new THREE.CameraHelper( light.shadowCamera );
+				scene.add( light.cameraHelper );
 
-				/* output Triangle */
+			}
 
-				result.push( [ contour[ a ],
-					contour[ b ],
-					contour[ c ] ] );
+			if ( light.isVirtual && virtualLight.originalCamera == camera ) {
 
+				updateShadowCamera( camera, light );
 
-				vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
+			}
 
-				/* remove v from the remaining polygon */
+			shadowMap = light.shadowMap;
+			shadowMatrix = light.shadowMatrix;
+			shadowCamera = light.shadowCamera;
 
-				for ( s = v, t = v + 1; t < nv; s++, t++ ) {
+			//
 
-					verts[ s ] = verts[ t ];
+			shadowCamera.position.setFromMatrixPosition( light.matrixWorld );
+			_matrixPosition.setFromMatrixPosition( light.target.matrixWorld );
+			shadowCamera.lookAt( _matrixPosition );
+			shadowCamera.updateMatrixWorld();
 
-				}
+			shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
 
-				nv --;
+			//
 
-				/* reset error detection counter */
+			if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible;
+			if ( light.shadowCameraVisible ) light.cameraHelper.update();
 
-				count = 2 * nv;
+			// compute shadow matrix
 
-			}
+			shadowMatrix.set(
+				0.5, 0.0, 0.0, 0.5,
+				0.0, 0.5, 0.0, 0.5,
+				0.0, 0.0, 0.5, 0.5,
+				0.0, 0.0, 0.0, 1.0
+			);
 
-		}
+			shadowMatrix.multiply( shadowCamera.projectionMatrix );
+			shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
 
-		if ( indices ) return vertIndices;
-		return result;
+			// update camera matrices and frustum
 
-	};
+			_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
+			_frustum.setFromMatrix( _projScreenMatrix );
 
-	// calculate area of the contour polygon
+			// render shadow map
 
-	var area = function ( contour ) {
+			_renderer.setRenderTarget( shadowMap );
+			_renderer.clear();
 
-		var n = contour.length;
-		var a = 0.0;
+			// set object matrices & frustum culling
 
-		for ( var p = n - 1, q = 0; q < n; p = q ++ ) {
+			_renderList.length = 0;
 
-			a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
+			projectObject( scene, scene, shadowCamera );
 
-		}
 
-		return a * 0.5;
+			// render regular objects
 
-	};
+			var objectMaterial, useMorphing, useSkinning;
 
-	var snip = function ( contour, u, v, w, n, verts ) {
+			for ( j = 0, jl = _renderList.length; j < jl; j ++ ) {
 
-		var p;
-		var ax, ay, bx, by;
-		var cx, cy, px, py;
+				webglObject = _renderList[ j ];
 
-		ax = contour[ verts[ u ] ].x;
-		ay = contour[ verts[ u ] ].y;
+				object = webglObject.object;
+				buffer = webglObject.buffer;
 
-		bx = contour[ verts[ v ] ].x;
-		by = contour[ verts[ v ] ].y;
+				// culling is overriden globally for all objects
+				// while rendering depth map
 
-		cx = contour[ verts[ w ] ].x;
-		cy = contour[ verts[ w ] ].y;
+				// need to deal with MeshFaceMaterial somehow
+				// in that case just use the first of material.materials for now
+				// (proper solution would require to break objects by materials
+				//  similarly to regular rendering and then set corresponding
+				//  depth materials per each chunk instead of just once per object)
 
-		if ( EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false;
+				objectMaterial = getObjectMaterial( object );
 
-		var aX, aY, bX, bY, cX, cY;
-		var apx, apy, bpx, bpy, cpx, cpy;
-		var cCROSSap, bCROSScp, aCROSSbp;
+				useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
+				useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning;
 
-		aX = cx - bx;  aY = cy - by;
-		bX = ax - cx;  bY = ay - cy;
-		cX = bx - ax;  cY = by - ay;
+				if ( object.customDepthMaterial ) {
 
-		for ( p = 0; p < n; p ++ ) {
+					material = object.customDepthMaterial;
 
-			px = contour[ verts[ p ] ].x
-			py = contour[ verts[ p ] ].y
+				} else if ( useSkinning ) {
 
-			if ( ( ( px === ax ) && ( py === ay ) ) ||
-				 ( ( px === bx ) && ( py === by ) ) ||
-				 ( ( px === cx ) && ( py === cy ) ) )	continue;
+					material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;
 
-			apx = px - ax;  apy = py - ay;
-			bpx = px - bx;  bpy = py - by;
-			cpx = px - cx;  cpy = py - cy;
+				} else if ( useMorphing ) {
 
-			// see if p is inside triangle abc
+					material = _depthMaterialMorph;
 
-			aCROSSbp = aX * bpy - aY * bpx;
-			cCROSSap = cX * apy - cY * apx;
-			bCROSScp = bX * cpy - bY * cpx;
+				} else {
 
-			if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) return false;
+					material = _depthMaterial;
 
-		}
+				}
 
-		return true;
+				_renderer.setMaterialFaces( objectMaterial );
 
-	};
+				if ( buffer instanceof THREE.BufferGeometry ) {
 
+					_renderer.renderBufferDirect( shadowCamera, _lights, fog, material, buffer, object );
 
-	namespace.Triangulate = process;
-	namespace.Triangulate.area = area;
+				} else {
 
-	return namespace;
+					_renderer.renderBuffer( shadowCamera, _lights, fog, material, buffer, object );
 
-} )( THREE.FontUtils );
+				}
 
-// To use the typeface.js face files, hook up the API
-self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace };
-THREE.typeface_js = self._typeface_js;
+			}
 
-// File:src/extras/audio/Audio.js
+			// set matrices and render immediate objects
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+			for ( j = 0, jl = _webglObjectsImmediate.length; j < jl; j ++ ) {
 
-THREE.Audio = function ( listener ) {
+				webglObject = _webglObjectsImmediate[ j ];
+				object = webglObject.object;
 
-	THREE.Object3D.call( this );
+				if ( object.visible && object.castShadow ) {
 
-	this.type = 'Audio';
+					object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
 
-	this.context = listener.context;
-	this.source = this.context.createBufferSource();
+					_renderer.renderImmediateObject( shadowCamera, _lights, fog, _depthMaterial, object );
 
-	this.gain = this.context.createGain();
-	this.gain.connect( this.context.destination );
+				}
 
-	this.panner = this.context.createPanner();
-	this.panner.connect( this.gain );
+			}
 
-};
+		}
 
-THREE.Audio.prototype = Object.create( THREE.Object3D.prototype );
+		// restore GL state
 
-THREE.Audio.prototype.load = function ( file ) {
+		var clearColor = _renderer.getClearColor(),
+		clearAlpha = _renderer.getClearAlpha();
 
-	var scope = this;
+		_gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
+		_gl.enable( _gl.BLEND );
 
-	var request = new XMLHttpRequest();
-	request.open( 'GET', file, true );
-	request.responseType = 'arraybuffer';
-	request.onload = function ( e ) {
+		if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
 
-		scope.context.decodeAudioData( this.response, function ( buffer ) {
+			_gl.cullFace( _gl.BACK );
 
-			scope.source.buffer = buffer;
-			scope.source.connect( scope.panner );
-			scope.source.start( 0 );
+		}
 
-		} );
+		_renderer.resetGLState();
 
 	};
-	request.send();
 
-	return this;
+	function projectObject( scene, object, shadowCamera ){
 
-};
+		if ( object.visible ) {
 
-THREE.Audio.prototype.setLoop = function ( value ) {
+			var webglObjects = _webglObjects[ object.id ];
 
-	this.source.loop = value;
+			if ( webglObjects && object.castShadow && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) {
 
-};
+				for ( var i = 0, l = webglObjects.length; i < l; i ++ ) {
 
-THREE.Audio.prototype.setRefDistance = function ( value ) {
+					var webglObject = webglObjects[ i ];
 
-	this.panner.refDistance = value;
+					object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
+					_renderList.push( webglObject );
 
-};
+				}
 
-THREE.Audio.prototype.setRolloffFactor = function ( value ) {
+			}
 
-	this.panner.rolloffFactor = value;
+			for ( var i = 0, l = object.children.length; i < l; i ++ ) {
 
-};
+				projectObject( scene, object.children[ i ], shadowCamera );
 
-THREE.Audio.prototype.updateMatrixWorld = ( function () {
+			}
 
-	var position = new THREE.Vector3();
+		}
 
-	return function ( force ) {
+	}
 
-		THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
+	function createVirtualLight( light, cascade ) {
 
-		position.setFromMatrixPosition( this.matrixWorld );
+		var virtualLight = new THREE.DirectionalLight();
 
-		this.panner.setPosition( position.x, position.y, position.z );
+		virtualLight.isVirtual = true;
 
-	};
+		virtualLight.onlyShadow = true;
+		virtualLight.castShadow = true;
 
-} )();
+		virtualLight.shadowCameraNear = light.shadowCameraNear;
+		virtualLight.shadowCameraFar = light.shadowCameraFar;
 
-// File:src/extras/audio/AudioListener.js
+		virtualLight.shadowCameraLeft = light.shadowCameraLeft;
+		virtualLight.shadowCameraRight = light.shadowCameraRight;
+		virtualLight.shadowCameraBottom = light.shadowCameraBottom;
+		virtualLight.shadowCameraTop = light.shadowCameraTop;
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+		virtualLight.shadowCameraVisible = light.shadowCameraVisible;
 
-THREE.AudioListener = function () {
+		virtualLight.shadowDarkness = light.shadowDarkness;
 
-	THREE.Object3D.call( this );
+		virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
+		virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ];
+		virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ];
 
-	this.type = 'AudioListener';
+		virtualLight.pointsWorld = [];
+		virtualLight.pointsFrustum = [];
 
-	this.context = new ( window.AudioContext || window.webkitAudioContext )();
+		var pointsWorld = virtualLight.pointsWorld,
+			pointsFrustum = virtualLight.pointsFrustum;
 
-};
+		for ( var i = 0; i < 8; i ++ ) {
 
-THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype );
+			pointsWorld[ i ] = new THREE.Vector3();
+			pointsFrustum[ i ] = new THREE.Vector3();
 
-THREE.AudioListener.prototype.updateMatrixWorld = ( function () {
+		}
 
-	var position = new THREE.Vector3();
-	var quaternion = new THREE.Quaternion();
-	var scale = new THREE.Vector3();
+		var nearZ = light.shadowCascadeNearZ[ cascade ];
+		var farZ = light.shadowCascadeFarZ[ cascade ];
 
-	var orientation = new THREE.Vector3();
-	var velocity = new THREE.Vector3();
+		pointsFrustum[ 0 ].set( - 1, - 1, nearZ );
+		pointsFrustum[ 1 ].set(  1, - 1, nearZ );
+		pointsFrustum[ 2 ].set( - 1,  1, nearZ );
+		pointsFrustum[ 3 ].set(  1,  1, nearZ );
 
-	var positionPrev = new THREE.Vector3();
+		pointsFrustum[ 4 ].set( - 1, - 1, farZ );
+		pointsFrustum[ 5 ].set(  1, - 1, farZ );
+		pointsFrustum[ 6 ].set( - 1,  1, farZ );
+		pointsFrustum[ 7 ].set(  1,  1, farZ );
 
-	return function ( force ) {
+		return virtualLight;
 
-		THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
+	}
 
-		var listener = this.context.listener;
+	// Synchronize virtual light with the original light
 
-		this.matrixWorld.decompose( position, quaternion, scale );
+	function updateVirtualLight( light, cascade ) {
 
-		orientation.set( 0, 0, -1 ).applyQuaternion( quaternion );
-		velocity.subVectors( position, positionPrev );
+		var virtualLight = light.shadowCascadeArray[ cascade ];
 
-		listener.setPosition( position.x, position.y, position.z );
-		listener.setOrientation( orientation.x, orientation.y, orientation.z, this.up.x, this.up.y, this.up.z );
-		listener.setVelocity( velocity.x, velocity.y, velocity.z );
+		virtualLight.position.copy( light.position );
+		virtualLight.target.position.copy( light.target.position );
+		virtualLight.lookAt( virtualLight.target );
 
-		positionPrev.copy( position );
+		virtualLight.shadowCameraVisible = light.shadowCameraVisible;
+		virtualLight.shadowDarkness = light.shadowDarkness;
 
-	};
+		virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
 
-} )();
+		var nearZ = light.shadowCascadeNearZ[ cascade ];
+		var farZ = light.shadowCascadeFarZ[ cascade ];
 
-// File:src/extras/core/Curve.js
+		var pointsFrustum = virtualLight.pointsFrustum;
 
-/**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- * Extensible curve object
- *
- * Some common of Curve methods
- * .getPoint(t), getTangent(t)
- * .getPointAt(u), getTagentAt(u)
- * .getPoints(), .getSpacedPoints()
- * .getLength()
- * .updateArcLengths()
- *
- * This following classes subclasses THREE.Curve:
- *
- * -- 2d classes --
- * THREE.LineCurve
- * THREE.QuadraticBezierCurve
- * THREE.CubicBezierCurve
- * THREE.SplineCurve
- * THREE.ArcCurve
- * THREE.EllipseCurve
- *
- * -- 3d classes --
- * THREE.LineCurve3
- * THREE.QuadraticBezierCurve3
- * THREE.CubicBezierCurve3
- * THREE.SplineCurve3
- * THREE.ClosedSplineCurve3
- *
- * A series of curves can be represented as a THREE.CurvePath
- *
- **/
+		pointsFrustum[ 0 ].z = nearZ;
+		pointsFrustum[ 1 ].z = nearZ;
+		pointsFrustum[ 2 ].z = nearZ;
+		pointsFrustum[ 3 ].z = nearZ;
 
-/**************************************************************
- *	Abstract Curve base class
- **************************************************************/
+		pointsFrustum[ 4 ].z = farZ;
+		pointsFrustum[ 5 ].z = farZ;
+		pointsFrustum[ 6 ].z = farZ;
+		pointsFrustum[ 7 ].z = farZ;
 
-THREE.Curve = function () {
+	}
 
-};
+	// Fit shadow camera's ortho frustum to camera frustum
 
-// Virtual base class method to overwrite and implement in subclasses
-//	- t [0 .. 1]
+	function updateShadowCamera( camera, light ) {
 
-THREE.Curve.prototype.getPoint = function ( t ) {
+		var shadowCamera = light.shadowCamera,
+			pointsFrustum = light.pointsFrustum,
+			pointsWorld = light.pointsWorld;
 
-	console.log( "Warning, getPoint() not implemented!" );
-	return null;
+		_min.set( Infinity, Infinity, Infinity );
+		_max.set( - Infinity, - Infinity, - Infinity );
 
-};
+		for ( var i = 0; i < 8; i ++ ) {
 
-// Get point at relative position in curve according to arc length
-// - u [0 .. 1]
+			var p = pointsWorld[ i ];
 
-THREE.Curve.prototype.getPointAt = function ( u ) {
+			p.copy( pointsFrustum[ i ] );
+			p.unproject( camera );
 
-	var t = this.getUtoTmapping( u );
-	return this.getPoint( t );
+			p.applyMatrix4( shadowCamera.matrixWorldInverse );
 
-};
+			if ( p.x < _min.x ) _min.x = p.x;
+			if ( p.x > _max.x ) _max.x = p.x;
 
-// Get sequence of points using getPoint( t )
+			if ( p.y < _min.y ) _min.y = p.y;
+			if ( p.y > _max.y ) _max.y = p.y;
 
-THREE.Curve.prototype.getPoints = function ( divisions ) {
+			if ( p.z < _min.z ) _min.z = p.z;
+			if ( p.z > _max.z ) _max.z = p.z;
 
-	if ( ! divisions ) divisions = 5;
+		}
 
-	var d, pts = [];
+		shadowCamera.left = _min.x;
+		shadowCamera.right = _max.x;
+		shadowCamera.top = _max.y;
+		shadowCamera.bottom = _min.y;
 
-	for ( d = 0; d <= divisions; d ++ ) {
+		// can't really fit near/far
+		//shadowCamera.near = _min.z;
+		//shadowCamera.far = _max.z;
 
-		pts.push( this.getPoint( d / divisions ) );
+		shadowCamera.updateProjectionMatrix();
 
 	}
 
-	return pts;
-
-};
+	// For the moment just ignore objects that have multiple materials with different animation methods
+	// Only the first material will be taken into account for deciding which depth material to use for shadow maps
 
-// Get sequence of points using getPointAt( u )
+	function getObjectMaterial( object ) {
 
-THREE.Curve.prototype.getSpacedPoints = function ( divisions ) {
+		return object.material instanceof THREE.MeshFaceMaterial
+			? object.material.materials[ 0 ]
+			: object.material;
 
-	if ( ! divisions ) divisions = 5;
+	};
 
-	var d, pts = [];
+};
 
-	for ( d = 0; d <= divisions; d ++ ) {
+// File:src/renderers/webgl/plugins/SpritePlugin.js
 
-		pts.push( this.getPointAt( d / divisions ) );
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-	}
+THREE.SpritePlugin = function ( renderer, sprites ) {
 
-	return pts;
+	var gl = renderer.context;
 
-};
+	var vertexBuffer, elementBuffer;
+	var program, attributes, uniforms;
 
-// Get total curve arc length
+	var texture;
+	
+	var init = function () {
 
-THREE.Curve.prototype.getLength = function () {
+		var vertices = new Float32Array( [
+			- 0.5, - 0.5,  0, 0,
+			  0.5, - 0.5,  1, 0,
+			  0.5,   0.5,  1, 1,
+			- 0.5,   0.5,  0, 1
+		] );
 
-	var lengths = this.getLengths();
-	return lengths[ lengths.length - 1 ];
+		var faces = new Uint16Array( [
+			0, 1, 2,
+			0, 2, 3
+		] );
 
-};
+		vertexBuffer  = gl.createBuffer();
+		elementBuffer = gl.createBuffer();
 
-// Get list of cumulative segment lengths
+		gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+		gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
 
-THREE.Curve.prototype.getLengths = function ( divisions ) {
+		gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+		gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
 
-	if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200;
+		program = createProgram();
 
-	if ( this.cacheArcLengths
-		&& ( this.cacheArcLengths.length == divisions + 1 )
-		&& ! this.needsUpdate) {
+		attributes = {
+			position:			gl.getAttribLocation ( program, 'position' ),
+			uv:					gl.getAttribLocation ( program, 'uv' )
+		};
 
-		//console.log( "cached", this.cacheArcLengths );
-		return this.cacheArcLengths;
+		uniforms = {
+			uvOffset:			gl.getUniformLocation( program, 'uvOffset' ),
+			uvScale:			gl.getUniformLocation( program, 'uvScale' ),
 
-	}
+			rotation:			gl.getUniformLocation( program, 'rotation' ),
+			scale:				gl.getUniformLocation( program, 'scale' ),
 
-	this.needsUpdate = false;
+			color:				gl.getUniformLocation( program, 'color' ),
+			map:				gl.getUniformLocation( program, 'map' ),
+			opacity:			gl.getUniformLocation( program, 'opacity' ),
 
-	var cache = [];
-	var current, last = this.getPoint( 0 );
-	var p, sum = 0;
+			modelViewMatrix: 	gl.getUniformLocation( program, 'modelViewMatrix' ),
+			projectionMatrix:	gl.getUniformLocation( program, 'projectionMatrix' ),
 
-	cache.push( 0 );
+			fogType:			gl.getUniformLocation( program, 'fogType' ),
+			fogDensity:			gl.getUniformLocation( program, 'fogDensity' ),
+			fogNear:			gl.getUniformLocation( program, 'fogNear' ),
+			fogFar:				gl.getUniformLocation( program, 'fogFar' ),
+			fogColor:			gl.getUniformLocation( program, 'fogColor' ),
 
-	for ( p = 1; p <= divisions; p ++ ) {
+			alphaTest:			gl.getUniformLocation( program, 'alphaTest' )
+		};
 
-		current = this.getPoint ( p / divisions );
-		sum += current.distanceTo( last );
-		cache.push( sum );
-		last = current;
+		var canvas = document.createElement( 'canvas' );
+		canvas.width = 8;
+		canvas.height = 8;
 
-	}
+		var context = canvas.getContext( '2d' );
+		context.fillStyle = 'white';
+		context.fillRect( 0, 0, 8, 8 );
 
-	this.cacheArcLengths = cache;
+		texture = new THREE.Texture( canvas );
+		texture.needsUpdate = true;
 
-	return cache; // { sums: cache, sum:sum }; Sum is in the last element.
+	};
 
-};
+	this.render = function ( scene, camera ) {
 
+		if ( sprites.length === 0 ) return;
 
-THREE.Curve.prototype.updateArcLengths = function() {
-	this.needsUpdate = true;
-	this.getLengths();
-};
+		// setup gl
 
-// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance
+		if ( program === undefined ) {
 
-THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) {
+			init();
 
-	var arcLengths = this.getLengths();
+		}
 
-	var i = 0, il = arcLengths.length;
+		gl.useProgram( program );
 
-	var targetArcLength; // The targeted u distance value to get
+		gl.enableVertexAttribArray( attributes.position );
+		gl.enableVertexAttribArray( attributes.uv );
 
-	if ( distance ) {
+		gl.disable( gl.CULL_FACE );
+		gl.enable( gl.BLEND );
 
-		targetArcLength = distance;
+		gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
+		gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 );
+		gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
 
-	} else {
+		gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
 
-		targetArcLength = u * arcLengths[ il - 1 ];
+		gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
 
-	}
+		gl.activeTexture( gl.TEXTURE0 );
+		gl.uniform1i( uniforms.map, 0 );
 
-	//var time = Date.now();
+		var oldFogType = 0;
+		var sceneFogType = 0;
+		var fog = scene.fog;
 
-	// binary search for the index with largest value smaller than target u distance
+		if ( fog ) {
 
-	var low = 0, high = il - 1, comparison;
+			gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );
 
-	while ( low <= high ) {
+			if ( fog instanceof THREE.Fog ) {
 
-		i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
+				gl.uniform1f( uniforms.fogNear, fog.near );
+				gl.uniform1f( uniforms.fogFar, fog.far );
 
-		comparison = arcLengths[ i ] - targetArcLength;
+				gl.uniform1i( uniforms.fogType, 1 );
+				oldFogType = 1;
+				sceneFogType = 1;
 
-		if ( comparison < 0 ) {
+			} else if ( fog instanceof THREE.FogExp2 ) {
 
-			low = i + 1;
-			continue;
+				gl.uniform1f( uniforms.fogDensity, fog.density );
 
-		} else if ( comparison > 0 ) {
+				gl.uniform1i( uniforms.fogType, 2 );
+				oldFogType = 2;
+				sceneFogType = 2;
 
-			high = i - 1;
-			continue;
+			}
 
 		} else {
 
-			high = i;
-			break;
-
-			// DONE
+			gl.uniform1i( uniforms.fogType, 0 );
+			oldFogType = 0;
+			sceneFogType = 0;
 
 		}
 
-	}
 
-	i = high;
+		// update positions and sort
 
-	//console.log('b' , i, low, high, Date.now()- time);
+		for ( var i = 0, l = sprites.length; i < l; i ++ ) {
 
-	if ( arcLengths[ i ] == targetArcLength ) {
+			var sprite = sprites[ i ];
 
-		var t = i / ( il - 1 );
-		return t;
+			sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );
 
-	}
+			if ( sprite.renderDepth === null ) {
 
-	// we could get finer grain at lengths, or use simple interpolatation between two points
+				sprite.z = - sprite._modelViewMatrix.elements[ 14 ];
 
-	var lengthBefore = arcLengths[ i ];
-    var lengthAfter = arcLengths[ i + 1 ];
+			} else {
 
-    var segmentLength = lengthAfter - lengthBefore;
+				sprite.z = sprite.renderDepth;
 
-    // determine where we are between the 'before' and 'after' points
+			}
 
-    var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
+		}
 
-    // add that fractional amount to t
+		sprites.sort( painterSortStable );
 
-    var t = ( i + segmentFraction ) / ( il -1 );
+		// render all sprites
 
-	return t;
+		var scale = [];
 
-};
+		for ( var i = 0, l = sprites.length; i < l; i ++ ) {
 
-// Returns a unit vector tangent at t
-// In case any sub curve does not implement its tangent derivation,
-// 2 points a small delta apart will be used to find its gradient
-// which seems to give a reasonable approximation
+			var sprite = sprites[ i ];
+			var material = sprite.material;
 
-THREE.Curve.prototype.getTangent = function( t ) {
+			gl.uniform1f( uniforms.alphaTest, material.alphaTest );
+			gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements );
 
-	var delta = 0.0001;
-	var t1 = t - delta;
-	var t2 = t + delta;
+			scale[ 0 ] = sprite.scale.x;
+			scale[ 1 ] = sprite.scale.y;
 
-	// Capping in case of danger
+			var fogType = 0;
 
-	if ( t1 < 0 ) t1 = 0;
-	if ( t2 > 1 ) t2 = 1;
+			if ( scene.fog && material.fog ) {
 
-	var pt1 = this.getPoint( t1 );
-	var pt2 = this.getPoint( t2 );
+				fogType = sceneFogType;
 
-	var vec = pt2.clone().sub(pt1);
-	return vec.normalize();
+			}
 
-};
+			if ( oldFogType !== fogType ) {
 
+				gl.uniform1i( uniforms.fogType, fogType );
+				oldFogType = fogType;
 
-THREE.Curve.prototype.getTangentAt = function ( u ) {
+			}
 
-	var t = this.getUtoTmapping( u );
-	return this.getTangent( t );
+			if ( material.map !== null ) {
 
-};
+				gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y );
+				gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y );
 
+			} else {
 
+				gl.uniform2f( uniforms.uvOffset, 0, 0 );
+				gl.uniform2f( uniforms.uvScale, 1, 1 );
 
+			}
 
+			gl.uniform1f( uniforms.opacity, material.opacity );
+			gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );
 
-/**************************************************************
- *	Utils
- **************************************************************/
+			gl.uniform1f( uniforms.rotation, material.rotation );
+			gl.uniform2fv( uniforms.scale, scale );
 
-THREE.Curve.Utils = {
+			renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
+			renderer.setDepthTest( material.depthTest );
+			renderer.setDepthWrite( material.depthWrite );
 
-	tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
+			if ( material.map && material.map.image && material.map.image.width ) {
 
-		return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
+				renderer.setTexture( material.map, 0 );
 
-	},
+			} else {
 
-	// Puay Bing, thanks for helping with this derivative!
+				renderer.setTexture( texture, 0 );
 
-	tangentCubicBezier: function (t, p0, p1, p2, p3 ) {
+			}
 
-		return - 3 * p0 * (1 - t) * (1 - t)  +
-			3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) +
-			6 * t *  p2 * (1-t) - 3 * t * t * p2 +
-			3 * t * t * p3;
+			gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
 
-	},
+		}
 
-	tangentSpline: function ( t, p0, p1, p2, p3 ) {
+		// restore gl
 
-		// To check if my formulas are correct
+		gl.enable( gl.CULL_FACE );
+		
+		renderer.resetGLState();
 
-		var h00 = 6 * t * t - 6 * t; 	// derived from 2t^3 − 3t^2 + 1
-		var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t
-		var h01 = - 6 * t * t + 6 * t; 	// − 2t3 + 3t2
-		var h11 = 3 * t * t - 2 * t;	// t3 − t2
+	};
 
-		return h00 + h10 + h01 + h11;
+	function createProgram () {
 
-	},
+		var program = gl.createProgram();
 
-	// Catmull-Rom
+		var vertexShader = gl.createShader( gl.VERTEX_SHADER );
+		var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
 
-	interpolate: function( p0, p1, p2, p3, t ) {
+		gl.shaderSource( vertexShader, [
 
-		var v0 = ( p2 - p0 ) * 0.5;
-		var v1 = ( p3 - p1 ) * 0.5;
-		var t2 = t * t;
-		var t3 = t * t2;
-		return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
+			'precision ' + renderer.getPrecision() + ' float;',
 
-	}
+			'uniform mat4 modelViewMatrix;',
+			'uniform mat4 projectionMatrix;',
+			'uniform float rotation;',
+			'uniform vec2 scale;',
+			'uniform vec2 uvOffset;',
+			'uniform vec2 uvScale;',
 
-};
+			'attribute vec2 position;',
+			'attribute vec2 uv;',
 
+			'varying vec2 vUV;',
 
-// TODO: Transformation for Curves?
+			'void main() {',
 
-/**************************************************************
- *	3D Curves
- **************************************************************/
+				'vUV = uvOffset + uv * uvScale;',
 
-// A Factory method for creating new curve subclasses
+				'vec2 alignedPosition = position * scale;',
 
-THREE.Curve.create = function ( constructor, getPointFunc ) {
+				'vec2 rotatedPosition;',
+				'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',
+				'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',
 
-	constructor.prototype = Object.create( THREE.Curve.prototype );
-	constructor.prototype.getPoint = getPointFunc;
+				'vec4 finalPosition;',
 
-	return constructor;
+				'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',
+				'finalPosition.xy += rotatedPosition;',
+				'finalPosition = projectionMatrix * finalPosition;',
 
-};
+				'gl_Position = finalPosition;',
 
-// File:src/extras/core/CurvePath.js
+			'}'
 
-/**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- *
- **/
+		].join( '\n' ) );
 
-/**************************************************************
- *	Curved Path - a curve path is simply a array of connected
- *  curves, but retains the api of a curve
- **************************************************************/
+		gl.shaderSource( fragmentShader, [
 
-THREE.CurvePath = function () {
+			'precision ' + renderer.getPrecision() + ' float;',
 
-	this.curves = [];
-	this.bends = [];
-	
-	this.autoClose = false; // Automatically closes the path
-};
+			'uniform vec3 color;',
+			'uniform sampler2D map;',
+			'uniform float opacity;',
 
-THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype );
+			'uniform int fogType;',
+			'uniform vec3 fogColor;',
+			'uniform float fogDensity;',
+			'uniform float fogNear;',
+			'uniform float fogFar;',
+			'uniform float alphaTest;',
 
-THREE.CurvePath.prototype.add = function ( curve ) {
+			'varying vec2 vUV;',
 
-	this.curves.push( curve );
+			'void main() {',
 
-};
+				'vec4 texture = texture2D( map, vUV );',
 
-THREE.CurvePath.prototype.checkConnection = function() {
-	// TODO
-	// If the ending of curve is not connected to the starting
-	// or the next curve, then, this is not a real path
-};
+				'if ( texture.a < alphaTest ) discard;',
 
-THREE.CurvePath.prototype.closePath = function() {
-	// TODO Test
-	// and verify for vector3 (needs to implement equals)
-	// Add a line curve if start and end of lines are not connected
-	var startPoint = this.curves[0].getPoint(0);
-	var endPoint = this.curves[this.curves.length-1].getPoint(1);
-	
-	if (! startPoint.equals(endPoint)) {
-		this.curves.push( new THREE.LineCurve(endPoint, startPoint) );
-	}
-	
-};
+				'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',
 
-// To get accurate point with reference to
-// entire path distance at time t,
-// following has to be done:
+				'if ( fogType > 0 ) {',
 
-// 1. Length of each sub path have to be known
-// 2. Locate and identify type of curve
-// 3. Get t for the curve
-// 4. Return curve.getPointAt(t')
+					'float depth = gl_FragCoord.z / gl_FragCoord.w;',
+					'float fogFactor = 0.0;',
 
-THREE.CurvePath.prototype.getPoint = function( t ) {
+					'if ( fogType == 1 ) {',
 
-	var d = t * this.getLength();
-	var curveLengths = this.getCurveLengths();
-	var i = 0, diff, curve;
+						'fogFactor = smoothstep( fogNear, fogFar, depth );',
 
-	// To think about boundaries points.
+					'} else {',
 
-	while ( i < curveLengths.length ) {
+						'const float LOG2 = 1.442695;',
+						'float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );',
+						'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',
 
-		if ( curveLengths[ i ] >= d ) {
+					'}',
 
-			diff = curveLengths[ i ] - d;
-			curve = this.curves[ i ];
+					'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );',
 
-			var u = 1 - diff / curve.getLength();
+				'}',
 
-			return curve.getPointAt( u );
+			'}'
 
-			break;
-		}
+		].join( '\n' ) );
 
-		i ++;
+		gl.compileShader( vertexShader );
+		gl.compileShader( fragmentShader );
 
-	}
+		gl.attachShader( program, vertexShader );
+		gl.attachShader( program, fragmentShader );
 
-	return null;
+		gl.linkProgram( program );
 
-	// loop where sum != 0, sum > d , sum+1 <d
+		return program;
 
-};
+	};
 
-/*
-THREE.CurvePath.prototype.getTangent = function( t ) {
-};*/
+	function painterSortStable ( a, b ) {
 
+		if ( a.z !== b.z ) {
 
-// We cannot use the default THREE.Curve getPoint() with getLength() because in
-// THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath
-// getPoint() depends on getLength
+			return b.z - a.z;
 
-THREE.CurvePath.prototype.getLength = function() {
+		} else {
 
-	var lens = this.getCurveLengths();
-	return lens[ lens.length - 1 ];
+			return b.id - a.id;
+
+		}
+
+	};
 
 };
 
-// Compute lengths and cache them
-// We cannot overwrite getLengths() because UtoT mapping uses it.
+// File:src/extras/GeometryUtils.js
 
-THREE.CurvePath.prototype.getCurveLengths = function() {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-	// We use cache values if curves and cache array are same length
+THREE.GeometryUtils = {
 
-	if ( this.cacheLengths && this.cacheLengths.length == this.curves.length ) {
+	merge: function ( geometry1, geometry2, materialIndexOffset ) {
 
-		return this.cacheLengths;
+		console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' );
 
-	};
+		var matrix;
 
-	// Get length of subsurve
-	// Push sums into cached array
+		if ( geometry2 instanceof THREE.Mesh ) {
 
-	var lengths = [], sums = 0;
-	var i, il = this.curves.length;
+			geometry2.matrixAutoUpdate && geometry2.updateMatrix();
 
-	for ( i = 0; i < il; i ++ ) {
+			matrix = geometry2.matrix;
+			geometry2 = geometry2.geometry;
 
-		sums += this.curves[ i ].getLength();
-		lengths.push( sums );
+		}
 
-	}
+		geometry1.merge( geometry2, matrix, materialIndexOffset );
 
-	this.cacheLengths = lengths;
+	},
 
-	return lengths;
+	center: function ( geometry ) {
 
-};
+		console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' );
+		return geometry.center();
 
+	}
 
+};
 
-// Returns min and max coordinates
+// File:src/extras/ImageUtils.js
 
-THREE.CurvePath.prototype.getBoundingBox = function () {
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author Daosheng Mu / https://github.com/DaoshengMu/
+ */
 
-	var points = this.getPoints();
+THREE.ImageUtils = {
 
-	var maxX, maxY, maxZ;
-	var minX, minY, minZ;
+	crossOrigin: undefined,
 
-	maxX = maxY = Number.NEGATIVE_INFINITY;
-	minX = minY = Number.POSITIVE_INFINITY;
+	loadTexture: function ( url, mapping, onLoad, onError ) {
 
-	var p, i, il, sum;
+		var loader = new THREE.ImageLoader();
+		loader.crossOrigin = this.crossOrigin;
 
-	var v3 = points[0] instanceof THREE.Vector3;
+		var texture = new THREE.Texture( undefined, mapping );
 
-	sum = v3 ? new THREE.Vector3() : new THREE.Vector2();
+		loader.load( url, function ( image ) {
 
-	for ( i = 0, il = points.length; i < il; i ++ ) {
+			texture.image = image;
+			texture.needsUpdate = true;
 
-		p = points[ i ];
+			if ( onLoad ) onLoad( texture );
 
-		if ( p.x > maxX ) maxX = p.x;
-		else if ( p.x < minX ) minX = p.x;
+		}, undefined, function ( event ) {
 
-		if ( p.y > maxY ) maxY = p.y;
-		else if ( p.y < minY ) minY = p.y;
+			if ( onError ) onError( event );
 
-		if ( v3 ) {
+		} );
 
-			if ( p.z > maxZ ) maxZ = p.z;
-			else if ( p.z < minZ ) minZ = p.z;
+		texture.sourceFile = url;
 
-		}
+		return texture;
 
-		sum.add( p );
+	},
 
-	}
+	loadTextureCube: function ( array, mapping, onLoad, onError ) {
 
-	var ret = {
+		var images = [];
 
-		minX: minX,
-		minY: minY,
-		maxX: maxX,
-		maxY: maxY
+		var loader = new THREE.ImageLoader();
+		loader.crossOrigin = this.crossOrigin;
 
-	};
+		var texture = new THREE.CubeTexture( images, mapping );
 
-	if ( v3 ) {
+		// no flipping needed for cube textures
 
-		ret.maxZ = maxZ;
-		ret.minZ = minZ;
+		texture.flipY = false;
 
-	}
+		var loaded = 0;
 
-	return ret;
+		var loadTexture = function ( i ) {
 
-};
+			loader.load( array[ i ], function ( image ) {
 
-/**************************************************************
- *	Create Geometries Helpers
- **************************************************************/
+				texture.images[ i ] = image;
 
-/// Generate geometry from path points (for Line or Points objects)
+				loaded += 1;
 
-THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) {
+				if ( loaded === 6 ) {
 
-	var pts = this.getPoints( divisions, true );
-	return this.createGeometry( pts );
+					texture.needsUpdate = true;
 
-};
+					if ( onLoad ) onLoad( texture );
 
-// Generate geometry from equidistance sampling along the path
+				}
 
-THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) {
+			} );
 
-	var pts = this.getSpacedPoints( divisions, true );
-	return this.createGeometry( pts );
+		}
 
-};
+		for ( var i = 0, il = array.length; i < il; ++ i ) {
 
-THREE.CurvePath.prototype.createGeometry = function( points ) {
+			loadTexture( i );
 
-	var geometry = new THREE.Geometry();
+		}
 
-	for ( var i = 0; i < points.length; i ++ ) {
+		return texture;
 
-		geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) );
+	},
 
-	}
+	loadCompressedTexture: function () {
 
-	return geometry;
+		console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' )
 
-};
+	},
 
+	loadCompressedTextureCube: function () {
 
-/**************************************************************
- *	Bend / Wrap Helper Methods
- **************************************************************/
+		console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' )
 
-// Wrap path / Bend modifiers?
+	},
 
-THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) {
+	getNormalMap: function ( image, depth ) {
 
-	this.bends.push( bendpath );
+		// Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/
 
-};
+		var cross = function ( a, b ) {
 
-THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) {
+			return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ];
 
-	var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints
-	var i, il;
+		}
 
-	if ( ! bends ) {
+		var subtract = function ( a, b ) {
 
-		bends = this.bends;
+			return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ];
 
-	}
+		}
 
-	for ( i = 0, il = bends.length; i < il; i ++ ) {
+		var normalize = function ( a ) {
 
-		oldPts = this.getWrapPoints( oldPts, bends[ i ] );
+			var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] );
+			return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ];
 
-	}
+		}
 
-	return oldPts;
+		depth = depth | 1;
 
-};
+		var width = image.width;
+		var height = image.height;
 
-THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) {
+		var canvas = document.createElement( 'canvas' );
+		canvas.width = width;
+		canvas.height = height;
 
-	var oldPts = this.getSpacedPoints( segments );
+		var context = canvas.getContext( '2d' );
+		context.drawImage( image, 0, 0 );
 
-	var i, il;
+		var data = context.getImageData( 0, 0, width, height ).data;
+		var imageData = context.createImageData( width, height );
+		var output = imageData.data;
 
-	if ( ! bends ) {
+		for ( var x = 0; x < width; x ++ ) {
 
-		bends = this.bends;
+			for ( var y = 0; y < height; y ++ ) {
 
-	}
+				var ly = y - 1 < 0 ? 0 : y - 1;
+				var uy = y + 1 > height - 1 ? height - 1 : y + 1;
+				var lx = x - 1 < 0 ? 0 : x - 1;
+				var ux = x + 1 > width - 1 ? width - 1 : x + 1;
 
-	for ( i = 0, il = bends.length; i < il; i ++ ) {
+				var points = [];
+				var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ];
+				points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] );
+				points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] );
+				points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] );
+				points.push( [  1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] );
+				points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] );
+				points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] );
+				points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] );
+				points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] );
 
-		oldPts = this.getWrapPoints( oldPts, bends[ i ] );
+				var normals = [];
+				var num_points = points.length;
 
-	}
+				for ( var i = 0; i < num_points; i ++ ) {
 
-	return oldPts;
+					var v1 = points[ i ];
+					var v2 = points[ ( i + 1 ) % num_points ];
+					v1 = subtract( v1, origin );
+					v2 = subtract( v2, origin );
+					normals.push( normalize( cross( v1, v2 ) ) );
 
-};
+				}
 
-// This returns getPoints() bend/wrapped around the contour of a path.
-// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html
+				var normal = [ 0, 0, 0 ];
 
-THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) {
+				for ( var i = 0; i < normals.length; i ++ ) {
 
-	var bounds = this.getBoundingBox();
+					normal[ 0 ] += normals[ i ][ 0 ];
+					normal[ 1 ] += normals[ i ][ 1 ];
+					normal[ 2 ] += normals[ i ][ 2 ];
 
-	var i, il, p, oldX, oldY, xNorm;
+				}
 
-	for ( i = 0, il = oldPts.length; i < il; i ++ ) {
+				normal[ 0 ] /= normals.length;
+				normal[ 1 ] /= normals.length;
+				normal[ 2 ] /= normals.length;
 
-		p = oldPts[ i ];
+				var idx = ( y * width + x ) * 4;
 
-		oldX = p.x;
-		oldY = p.y;
+				output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0;
+				output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0;
+				output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0;
+				output[ idx + 3 ] = 255;
 
-		xNorm = oldX / bounds.maxX;
+			}
 
-		// If using actual distance, for length > path, requires line extrusions
-		//xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance
+		}
 
-		xNorm = path.getUtoTmapping( xNorm, oldX );
+		context.putImageData( imageData, 0, 0 );
 
-		// check for out of bounds?
+		return canvas;
 
-		var pathPt = path.getPoint( xNorm );
-		var normal = path.getTangent( xNorm );
-		normal.set( - normal.y, normal.x ).multiplyScalar( oldY );
+	},
 
-		p.x = pathPt.x + normal.x;
-		p.y = pathPt.y + normal.y;
+	generateDataTexture: function ( width, height, color ) {
 
-	}
+		var size = width * height;
+		var data = new Uint8Array( 3 * size );
 
-	return oldPts;
+		var r = Math.floor( color.r * 255 );
+		var g = Math.floor( color.g * 255 );
+		var b = Math.floor( color.b * 255 );
 
-};
+		for ( var i = 0; i < size; i ++ ) {
 
+			data[ i * 3 ] 	   = r;
+			data[ i * 3 + 1 ] = g;
+			data[ i * 3 + 2 ] = b;
 
-// File:src/extras/core/Gyroscope.js
+		}
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
+		var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat );
+		texture.needsUpdate = true;
 
-THREE.Gyroscope = function () {
+		return texture;
 
-	THREE.Object3D.call( this );
+	}
 
 };
 
-THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype );
-
-THREE.Gyroscope.prototype.updateMatrixWorld = ( function () {
-
-	var translationObject = new THREE.Vector3();
-	var quaternionObject = new THREE.Quaternion();
-	var scaleObject = new THREE.Vector3();
-
-	var translationWorld = new THREE.Vector3();
-	var quaternionWorld = new THREE.Quaternion();
-	var scaleWorld = new THREE.Vector3();
-
-	return function ( force ) {
-
-		this.matrixAutoUpdate && this.updateMatrix();
+// File:src/extras/SceneUtils.js
 
-		// update matrixWorld
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-		if ( this.matrixWorldNeedsUpdate || force ) {
+THREE.SceneUtils = {
 
-			if ( this.parent ) {
+	createMultiMaterialObject: function ( geometry, materials ) {
 
-				this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+		var group = new THREE.Object3D();
 
-				this.matrixWorld.decompose( translationWorld, quaternionWorld, scaleWorld );
-				this.matrix.decompose( translationObject, quaternionObject, scaleObject );
+		for ( var i = 0, l = materials.length; i < l; i ++ ) {
 
-				this.matrixWorld.compose( translationWorld, quaternionObject, scaleWorld );
+			group.add( new THREE.Mesh( geometry, materials[ i ] ) );
 
+		}
 
-			} else {
+		return group;
 
-				this.matrixWorld.copy( this.matrix );
+	},
 
-			}
+	detach: function ( child, parent, scene ) {
 
+		child.applyMatrix( parent.matrixWorld );
+		parent.remove( child );
+		scene.add( child );
 
-			this.matrixWorldNeedsUpdate = false;
+	},
 
-			force = true;
+	attach: function ( child, scene, parent ) {
 
-		}
+		var matrixWorldInverse = new THREE.Matrix4();
+		matrixWorldInverse.getInverse( parent.matrixWorld );
+		child.applyMatrix( matrixWorldInverse );
 
-		// update children
+		scene.remove( child );
+		parent.add( child );
 
-		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+	}
 
-			this.children[ i ].updateMatrixWorld( force );
+};
 
-		}
-
-	};
-	
-}() );
-
-// File:src/extras/core/Path.js
+// File:src/extras/FontUtils.js
 
 /**
  * @author zz85 / http://www.lab4games.net/zz85/blog
- * Creates free form 2d path using series of points, lines or curves.
+ * @author alteredq / http://alteredqualia.com/
  *
- **/
+ * For Text operations in three.js (See TextGeometry)
+ *
+ * It uses techniques used in:
+ *
+ * 	typeface.js and canvastext
+ * 		For converting fonts and rendering with javascript
+ *		http://typeface.neocracy.org
+ *
+ *	Triangulation ported from AS3
+ *		Simple Polygon Triangulation
+ *		http://actionsnippet.com/?p=1462
+ *
+ * 	A Method to triangulate shapes with holes
+ *		http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/
+ *
+ */
 
-THREE.Path = function ( points ) {
+THREE.FontUtils = {
 
-	THREE.CurvePath.call(this);
+	faces: {},
 
-	this.actions = [];
+	// Just for now. face[weight][style]
 
-	if ( points ) {
+	face: 'helvetiker',
+	weight: 'normal',
+	style: 'normal',
+	size: 150,
+	divisions: 10,
 
-		this.fromPoints( points );
+	getFace: function () {
 
-	}
+		try {
 
-};
+			return this.faces[ this.face ][ this.weight ][ this.style ];
 
-THREE.Path.prototype = Object.create( THREE.CurvePath.prototype );
+		} catch (e) {
 
-THREE.PathActions = {
+			throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing."
 
-	MOVE_TO: 'moveTo',
-	LINE_TO: 'lineTo',
-	QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve
-	BEZIER_CURVE_TO: 'bezierCurveTo', 		// Bezier cubic curve
-	CSPLINE_THRU: 'splineThru',				// Catmull-rom spline
-	ARC: 'arc',								// Circle
-	ELLIPSE: 'ellipse'
-};
+		};
 
-// TODO Clean up PATH API
+	},
 
-// Create path using straight lines to connect all points
-// - vectors: array of Vector2
+	loadFace: function ( data ) {
 
-THREE.Path.prototype.fromPoints = function ( vectors ) {
+		var family = data.familyName.toLowerCase();
 
-	this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y );
+		var ThreeFont = this;
 
-	for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) {
+		ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
 
-		this.lineTo( vectors[ v ].x, vectors[ v ].y );
+		ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
+		ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
 
-	};
+		var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
 
-};
+		return data;
 
-// startPath() endPath()?
+	},
 
-THREE.Path.prototype.moveTo = function ( x, y ) {
+	drawText: function ( text ) {
 
-	var args = Array.prototype.slice.call( arguments );
-	this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } );
+		var characterPts = [], allPts = [];
 
-};
+		// RenderText
 
-THREE.Path.prototype.lineTo = function ( x, y ) {
+		var i, p,
+			face = this.getFace(),
+			scale = this.size / face.resolution,
+			offset = 0,
+			chars = String( text ).split( '' ),
+			length = chars.length;
 
-	var args = Array.prototype.slice.call( arguments );
+		var fontPaths = [];
 
-	var lastargs = this.actions[ this.actions.length - 1 ].args;
+		for ( i = 0; i < length; i ++ ) {
 
-	var x0 = lastargs[ lastargs.length - 2 ];
-	var y0 = lastargs[ lastargs.length - 1 ];
+			var path = new THREE.Path();
 
-	var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) );
-	this.curves.push( curve );
+			var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path );
+			offset += ret.offset;
 
-	this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } );
+			fontPaths.push( ret.path );
 
-};
+		}
 
-THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) {
+		// get the width
 
-	var args = Array.prototype.slice.call( arguments );
+		var width = offset / 2;
+		//
+		// for ( p = 0; p < allPts.length; p++ ) {
+		//
+		// 	allPts[ p ].x -= width;
+		//
+		// }
 
-	var lastargs = this.actions[ this.actions.length - 1 ].args;
+		//var extract = this.extractPoints( allPts, characterPts );
+		//extract.contour = allPts;
 
-	var x0 = lastargs[ lastargs.length - 2 ];
-	var y0 = lastargs[ lastargs.length - 1 ];
+		//extract.paths = fontPaths;
+		//extract.offset = width;
 
-	var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ),
-												new THREE.Vector2( aCPx, aCPy ),
-												new THREE.Vector2( aX, aY ) );
-	this.curves.push( curve );
+		return { paths: fontPaths, offset: width };
 
-	this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } );
+	},
 
-};
 
-THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y,
-											   aCP2x, aCP2y,
-											   aX, aY ) {
 
-	var args = Array.prototype.slice.call( arguments );
 
-	var lastargs = this.actions[ this.actions.length - 1 ].args;
+	extractGlyphPoints: function ( c, face, scale, offset, path ) {
 
-	var x0 = lastargs[ lastargs.length - 2 ];
-	var y0 = lastargs[ lastargs.length - 1 ];
+		var pts = [];
 
-	var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ),
-											new THREE.Vector2( aCP1x, aCP1y ),
-											new THREE.Vector2( aCP2x, aCP2y ),
-											new THREE.Vector2( aX, aY ) );
-	this.curves.push( curve );
+		var i, i2, divisions,
+			outline, action, length,
+			scaleX, scaleY,
+			x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2,
+			laste,
+			glyph = face.glyphs[ c ] || face.glyphs[ '?' ];
 
-	this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } );
+		if ( ! glyph ) return;
 
-};
+		if ( glyph.o ) {
 
-THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) {
+			outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
+			length = outline.length;
 
-	var args = Array.prototype.slice.call( arguments );
-	var lastargs = this.actions[ this.actions.length - 1 ].args;
+			scaleX = scale;
+			scaleY = scale;
 
-	var x0 = lastargs[ lastargs.length - 2 ];
-	var y0 = lastargs[ lastargs.length - 1 ];
-//---
-	var npts = [ new THREE.Vector2( x0, y0 ) ];
-	Array.prototype.push.apply( npts, pts );
+			for ( i = 0; i < length; ) {
 
-	var curve = new THREE.SplineCurve( npts );
-	this.curves.push( curve );
+				action = outline[ i ++ ];
 
-	this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } );
+				//console.log( action );
 
-};
+				switch ( action ) {
 
-// FUTURE: Change the API or follow canvas API?
+				case 'm':
 
-THREE.Path.prototype.arc = function ( aX, aY, aRadius,
-									  aStartAngle, aEndAngle, aClockwise ) {
+					// Move To
 
-	var lastargs = this.actions[ this.actions.length - 1].args;
-	var x0 = lastargs[ lastargs.length - 2 ];
-	var y0 = lastargs[ lastargs.length - 1 ];
+					x = outline[ i ++ ] * scaleX + offset;
+					y = outline[ i ++ ] * scaleY;
 
-	this.absarc(aX + x0, aY + y0, aRadius,
-		aStartAngle, aEndAngle, aClockwise );
+					path.moveTo( x, y );
+					break;
 
- };
+				case 'l':
 
- THREE.Path.prototype.absarc = function ( aX, aY, aRadius,
-									  aStartAngle, aEndAngle, aClockwise ) {
-	this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise);
- };
+					// Line To
 
-THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius,
-									  aStartAngle, aEndAngle, aClockwise ) {
+					x = outline[ i ++ ] * scaleX + offset;
+					y = outline[ i ++ ] * scaleY;
+					path.lineTo( x,y );
+					break;
 
-	var lastargs = this.actions[ this.actions.length - 1].args;
-	var x0 = lastargs[ lastargs.length - 2 ];
-	var y0 = lastargs[ lastargs.length - 1 ];
+				case 'q':
 
-	this.absellipse(aX + x0, aY + y0, xRadius, yRadius,
-		aStartAngle, aEndAngle, aClockwise );
+					// QuadraticCurveTo
 
- };
+					cpx  = outline[ i ++ ] * scaleX + offset;
+					cpy  = outline[ i ++ ] * scaleY;
+					cpx1 = outline[ i ++ ] * scaleX + offset;
+					cpy1 = outline[ i ++ ] * scaleY;
 
+					path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
 
-THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius,
-									  aStartAngle, aEndAngle, aClockwise ) {
+					laste = pts[ pts.length - 1 ];
 
-	var args = Array.prototype.slice.call( arguments );
-	var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius,
-									aStartAngle, aEndAngle, aClockwise );
-	this.curves.push( curve );
+					if ( laste ) {
 
-	var lastPoint = curve.getPoint(1);
-	args.push(lastPoint.x);
-	args.push(lastPoint.y);
+						cpx0 = laste.x;
+						cpy0 = laste.y;
 
-	this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } );
+						for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
 
- };
+							var t = i2 / divisions;
+							var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
+							var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
+					  }
 
-THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) {
+				  }
 
-	if ( ! divisions ) divisions = 40;
+				  break;
 
-	var points = [];
+				case 'b':
 
-	for ( var i = 0; i < divisions; i ++ ) {
+					// Cubic Bezier Curve
 
-		points.push( this.getPoint( i / divisions ) );
+					cpx  = outline[ i ++ ] *  scaleX + offset;
+					cpy  = outline[ i ++ ] *  scaleY;
+					cpx1 = outline[ i ++ ] *  scaleX + offset;
+					cpy1 = outline[ i ++ ] *  scaleY;
+					cpx2 = outline[ i ++ ] *  scaleX + offset;
+					cpy2 = outline[ i ++ ] *  scaleY;
 
-		//if( !this.getPoint( i / divisions ) ) throw "DIE";
+					path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
 
-	}
+					laste = pts[ pts.length - 1 ];
 
-	// if ( closedPath ) {
-	//
-	// 	points.push( points[ 0 ] );
-	//
-	// }
+					if ( laste ) {
 
-	return points;
-
-};
+						cpx0 = laste.x;
+						cpy0 = laste.y;
 
-/* Return an array of vectors based on contour of the path */
+						for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
 
-THREE.Path.prototype.getPoints = function( divisions, closedPath ) {
+							var t = i2 / divisions;
+							var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
+							var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
 
-	if (this.useSpacedPoints) {
-		console.log('tata');
-		return this.getSpacedPoints( divisions, closedPath );
-	}
+						}
 
-	divisions = divisions || 12;
+					}
 
-	var points = [];
+					break;
 
-	var i, il, item, action, args;
-	var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0,
-		laste, j,
-		t, tx, ty;
+				}
 
-	for ( i = 0, il = this.actions.length; i < il; i ++ ) {
+			}
+		}
 
-		item = this.actions[ i ];
 
-		action = item.action;
-		args = item.args;
 
-		switch( action ) {
+		return { offset: glyph.ha * scale, path:path };
+	}
 
-		case THREE.PathActions.MOVE_TO:
+};
 
-			points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
 
-			break;
+THREE.FontUtils.generateShapes = function ( text, parameters ) {
 
-		case THREE.PathActions.LINE_TO:
+	// Parameters 
 
-			points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
+	parameters = parameters || {};
 
-			break;
+	var size = parameters.size !== undefined ? parameters.size : 100;
+	var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4;
 
-		case THREE.PathActions.QUADRATIC_CURVE_TO:
+	var font = parameters.font !== undefined ? parameters.font : 'helvetiker';
+	var weight = parameters.weight !== undefined ? parameters.weight : 'normal';
+	var style = parameters.style !== undefined ? parameters.style : 'normal';
 
-			cpx  = args[ 2 ];
-			cpy  = args[ 3 ];
+	THREE.FontUtils.size = size;
+	THREE.FontUtils.divisions = curveSegments;
 
-			cpx1 = args[ 0 ];
-			cpy1 = args[ 1 ];
+	THREE.FontUtils.face = font;
+	THREE.FontUtils.weight = weight;
+	THREE.FontUtils.style = style;
 
-			if ( points.length > 0 ) {
+	// Get a Font data json object
 
-				laste = points[ points.length - 1 ];
+	var data = THREE.FontUtils.drawText( text );
 
-				cpx0 = laste.x;
-				cpy0 = laste.y;
+	var paths = data.paths;
+	var shapes = [];
 
-			} else {
+	for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
 
-				laste = this.actions[ i - 1 ].args;
+		Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
 
-				cpx0 = laste[ laste.length - 2 ];
-				cpy0 = laste[ laste.length - 1 ];
+	}
 
-			}
+	return shapes;
 
-			for ( j = 1; j <= divisions; j ++ ) {
+};
 
-				t = j / divisions;
 
-				tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
-				ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
+/**
+ * This code is a quick port of code written in C++ which was submitted to
+ * flipcode.com by John W. Ratcliff  // July 22, 2000
+ * See original code and more information here:
+ * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
+ *
+ * ported to actionscript by Zevan Rosser
+ * www.actionsnippet.com
+ *
+ * ported to javascript by Joshua Koo
+ * http://www.lab4games.net/zz85/blog
+ *
+ */
 
-				points.push( new THREE.Vector2( tx, ty ) );
 
-			}
+( function ( namespace ) {
 
-			break;
+	var EPSILON = 0.0000000001;
 
-		case THREE.PathActions.BEZIER_CURVE_TO:
+	// takes in an contour array and returns
 
-			cpx  = args[ 4 ];
-			cpy  = args[ 5 ];
+	var process = function ( contour, indices ) {
 
-			cpx1 = args[ 0 ];
-			cpy1 = args[ 1 ];
+		var n = contour.length;
 
-			cpx2 = args[ 2 ];
-			cpy2 = args[ 3 ];
+		if ( n < 3 ) return null;
 
-			if ( points.length > 0 ) {
+		var result = [],
+			verts = [],
+			vertIndices = [];
 
-				laste = points[ points.length - 1 ];
+		/* we want a counter-clockwise polygon in verts */
 
-				cpx0 = laste.x;
-				cpy0 = laste.y;
+		var u, v, w;
 
-			} else {
+		if ( area( contour ) > 0.0 ) {
 
-				laste = this.actions[ i - 1 ].args;
+			for ( v = 0; v < n; v ++ ) verts[ v ] = v;
 
-				cpx0 = laste[ laste.length - 2 ];
-				cpy0 = laste[ laste.length - 1 ];
+		} else {
 
-			}
+			for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v;
 
+		}
 
-			for ( j = 1; j <= divisions; j ++ ) {
+		var nv = n;
 
-				t = j / divisions;
+		/*  remove nv - 2 vertices, creating 1 triangle every time */
 
-				tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
-				ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
+		var count = 2 * nv;   /* error detection */
 
-				points.push( new THREE.Vector2( tx, ty ) );
+		for ( v = nv - 1; nv > 2; ) {
 
-			}
+			/* if we loop, it is probably a non-simple polygon */
 
-			break;
+			if ( ( count -- ) <= 0 ) {
 
-		case THREE.PathActions.CSPLINE_THRU:
+				//** Triangulate: ERROR - probable bad polygon!
 
-			laste = this.actions[ i - 1 ].args;
+				//throw ( "Warning, unable to triangulate polygon!" );
+				//return null;
+				// Sometimes warning is fine, especially polygons are triangulated in reverse.
+				console.log( 'Warning, unable to triangulate polygon!' );
 
-			var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] );
-			var spts = [ last ];
+				if ( indices ) return vertIndices;
+				return result;
 
-			var n = divisions * args[ 0 ].length;
+			}
 
-			spts = spts.concat( args[ 0 ] );
+			/* three consecutive vertices in current polygon, <u,v,w> */
 
-			var spline = new THREE.SplineCurve( spts );
+			u = v; 	 	if ( nv <= u ) u = 0;     /* previous */
+			v = u + 1;  if ( nv <= v ) v = 0;     /* new v    */
+			w = v + 1;  if ( nv <= w ) w = 0;     /* next     */
 
-			for ( j = 1; j <= n; j ++ ) {
+			if ( snip( contour, u, v, w, nv, verts ) ) {
 
-				points.push( spline.getPointAt( j / n ) ) ;
+				var a, b, c, s, t;
 
-			}
+				/* true names of the vertices */
 
-			break;
+				a = verts[ u ];
+				b = verts[ v ];
+				c = verts[ w ];
 
-		case THREE.PathActions.ARC:
+				/* output Triangle */
 
-			var aX = args[ 0 ], aY = args[ 1 ],
-				aRadius = args[ 2 ],
-				aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
-				aClockwise = !! args[ 5 ];
+				result.push( [ contour[ a ],
+					contour[ b ],
+					contour[ c ] ] );
 
-			var deltaAngle = aEndAngle - aStartAngle;
-			var angle;
-			var tdivisions = divisions * 2;
 
-			for ( j = 1; j <= tdivisions; j ++ ) {
+				vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
 
-				t = j / tdivisions;
+				/* remove v from the remaining polygon */
 
-				if ( ! aClockwise ) {
+				for ( s = v, t = v + 1; t < nv; s++, t++ ) {
 
-					t = 1 - t;
+					verts[ s ] = verts[ t ];
 
 				}
 
-				angle = aStartAngle + t * deltaAngle;
-
-				tx = aX + aRadius * Math.cos( angle );
-				ty = aY + aRadius * Math.sin( angle );
+				nv --;
 
-				//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
+				/* reset error detection counter */
 
-				points.push( new THREE.Vector2( tx, ty ) );
+				count = 2 * nv;
 
 			}
 
-			//console.log(points);
-
-		  break;
-		  
-		case THREE.PathActions.ELLIPSE:
-
-			var aX = args[ 0 ], aY = args[ 1 ],
-				xRadius = args[ 2 ],
-				yRadius = args[ 3 ],
-				aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
-				aClockwise = !! args[ 6 ];
+		}
 
+		if ( indices ) return vertIndices;
+		return result;
 
-			var deltaAngle = aEndAngle - aStartAngle;
-			var angle;
-			var tdivisions = divisions * 2;
+	};
 
-			for ( j = 1; j <= tdivisions; j ++ ) {
+	// calculate area of the contour polygon
 
-				t = j / tdivisions;
+	var area = function ( contour ) {
 
-				if ( ! aClockwise ) {
+		var n = contour.length;
+		var a = 0.0;
 
-					t = 1 - t;
+		for ( var p = n - 1, q = 0; q < n; p = q ++ ) {
 
-				}
+			a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
 
-				angle = aStartAngle + t * deltaAngle;
+		}
 
-				tx = aX + xRadius * Math.cos( angle );
-				ty = aY + yRadius * Math.sin( angle );
+		return a * 0.5;
 
-				//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
+	};
 
-				points.push( new THREE.Vector2( tx, ty ) );
+	var snip = function ( contour, u, v, w, n, verts ) {
 
-			}
+		var p;
+		var ax, ay, bx, by;
+		var cx, cy, px, py;
 
-			//console.log(points);
+		ax = contour[ verts[ u ] ].x;
+		ay = contour[ verts[ u ] ].y;
 
-		  break;
+		bx = contour[ verts[ v ] ].x;
+		by = contour[ verts[ v ] ].y;
 
-		} // end switch
+		cx = contour[ verts[ w ] ].x;
+		cy = contour[ verts[ w ] ].y;
 
-	}
+		if ( EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false;
 
+		var aX, aY, bX, bY, cX, cY;
+		var apx, apy, bpx, bpy, cpx, cpy;
+		var cCROSSap, bCROSScp, aCROSSbp;
 
+		aX = cx - bx;  aY = cy - by;
+		bX = ax - cx;  bY = ay - cy;
+		cX = bx - ax;  cY = by - ay;
 
-	// Normalize to remove the closing point by default.
-	var lastPoint = points[ points.length - 1];
-	var EPSILON = 0.0000000001;
-	if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON &&
-			 Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON)
-		points.splice( points.length - 1, 1);
-	if ( closedPath ) {
-
-		points.push( points[ 0 ] );
+		for ( p = 0; p < n; p ++ ) {
 
-	}
+			px = contour[ verts[ p ] ].x
+			py = contour[ verts[ p ] ].y
 
-	return points;
+			if ( ( ( px === ax ) && ( py === ay ) ) ||
+				 ( ( px === bx ) && ( py === by ) ) ||
+				 ( ( px === cx ) && ( py === cy ) ) )	continue;
 
-};
+			apx = px - ax;  apy = py - ay;
+			bpx = px - bx;  bpy = py - by;
+			cpx = px - cx;  cpy = py - cy;
 
-//
-// Breaks path into shapes
-//
-//	Assumptions (if parameter isCCW==true the opposite holds):
-//	- solid shapes are defined clockwise (CW)
-//	- holes are defined counterclockwise (CCW)
-//
-//	If parameter noHoles==true:
-//  - all subPaths are regarded as solid shapes
-//  - definition order CW/CCW has no relevance
-//
+			// see if p is inside triangle abc
 
-THREE.Path.prototype.toShapes = function( isCCW, noHoles ) {
+			aCROSSbp = aX * bpy - aY * bpx;
+			cCROSSap = cX * apy - cY * apx;
+			bCROSScp = bX * cpy - bY * cpx;
 
-	function extractSubpaths( inActions ) {
+			if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) return false;
 
-		var i, il, item, action, args;
+		}
 
-		var subPaths = [], lastPath = new THREE.Path();
+		return true;
 
-		for ( i = 0, il = inActions.length; i < il; i ++ ) {
+	};
 
-			item = inActions[ i ];
 
-			args = item.args;
-			action = item.action;
+	namespace.Triangulate = process;
+	namespace.Triangulate.area = area;
 
-			if ( action == THREE.PathActions.MOVE_TO ) {
+	return namespace;
 
-				if ( lastPath.actions.length != 0 ) {
+} )( THREE.FontUtils );
 
-					subPaths.push( lastPath );
-					lastPath = new THREE.Path();
+// To use the typeface.js face files, hook up the API
+self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace };
+THREE.typeface_js = self._typeface_js;
 
-				}
+// File:src/extras/audio/Audio.js
 
-			}
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-			lastPath[ action ].apply( lastPath, args );
+THREE.Audio = function ( listener ) {
 
-		}
+	THREE.Object3D.call( this );
 
-		if ( lastPath.actions.length != 0 ) {
+	this.type = 'Audio';
 
-			subPaths.push( lastPath );
+	this.context = listener.context;
+	this.source = this.context.createBufferSource();
 
-		}
+	this.gain = this.context.createGain();
+	this.gain.connect( this.context.destination );
 
-		// console.log(subPaths);
+	this.panner = this.context.createPanner();
+	this.panner.connect( this.gain );
 
-		return	subPaths;
-	}
+};
 
-	function toShapesNoHoles( inSubpaths ) {
+THREE.Audio.prototype = Object.create( THREE.Object3D.prototype );
 
-		var shapes = [];
+THREE.Audio.prototype.load = function ( file ) {
 
-		for ( var i = 0, il = inSubpaths.length; i < il; i ++ ) {
+	var scope = this;
 
-			var tmpPath = inSubpaths[ i ];
+	var request = new XMLHttpRequest();
+	request.open( 'GET', file, true );
+	request.responseType = 'arraybuffer';
+	request.onload = function ( e ) {
 
-			var tmpShape = new THREE.Shape();
-			tmpShape.actions = tmpPath.actions;
-			tmpShape.curves = tmpPath.curves;
+		scope.context.decodeAudioData( this.response, function ( buffer ) {
 
-			shapes.push( tmpShape );
-		}
+			scope.source.buffer = buffer;
+			scope.source.connect( scope.panner );
+			scope.source.start( 0 );
 
-		//console.log("shape", shapes);
+		} );
 
-		return shapes;
 	};
+	request.send();
 
-	function isPointInsidePolygon( inPt, inPolygon ) {
-		var EPSILON = 0.0000000001;
+	return this;
 
-		var polyLen = inPolygon.length;
+};
 
-		// inPt on polygon contour => immediate success    or
-		// toggling of inside/outside at every single! intersection point of an edge
-		//  with the horizontal line through inPt, left of inPt
-		//  not counting lowerY endpoints of edges and whole edges on that line
-		var inside = false;
-		for( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {
-			var edgeLowPt  = inPolygon[ p ];
-			var edgeHighPt = inPolygon[ q ];
+THREE.Audio.prototype.setLoop = function ( value ) {
 
-			var edgeDx = edgeHighPt.x - edgeLowPt.x;
-			var edgeDy = edgeHighPt.y - edgeLowPt.y;
+	this.source.loop = value;
 
-			if ( Math.abs(edgeDy) > EPSILON ) {			// not parallel
-				if ( edgeDy < 0 ) {
-					edgeLowPt  = inPolygon[ q ]; edgeDx = - edgeDx;
-					edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;
-				}
-				if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) 		continue;
+};
 
-				if ( inPt.y == edgeLowPt.y ) {
-					if ( inPt.x == edgeLowPt.x )		return	true;		// inPt is on contour ?
-					// continue;				// no intersection or edgeLowPt => doesn't count !!!
-				} else {
-					var perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y);
-					if ( perpEdge == 0 )				return	true;		// inPt is on contour ?
-					if ( perpEdge < 0 ) 				continue;
-					inside = ! inside;		// true intersection left of inPt
-				}
-			} else {		// parallel or colinear
-				if ( inPt.y != edgeLowPt.y ) 		continue;			// parallel
-				// egde lies on the same horizontal line as inPt
-				if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||
-					 ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) )		return	true;	// inPt: Point on contour !
-				// continue;
-			}
-		}
+THREE.Audio.prototype.setRefDistance = function ( value ) {
 
-		return	inside;
-	}
+	this.panner.refDistance = value;
 
+};
 
-	var subPaths = extractSubpaths( this.actions );
-	if ( subPaths.length == 0 ) return [];
+THREE.Audio.prototype.setRolloffFactor = function ( value ) {
 
-	if ( noHoles === true )	return	toShapesNoHoles( subPaths );
+	this.panner.rolloffFactor = value;
 
+};
 
-	var solid, tmpPath, tmpShape, shapes = [];
+THREE.Audio.prototype.updateMatrixWorld = ( function () {
 
-	if ( subPaths.length == 1) {
+	var position = new THREE.Vector3();
 
-		tmpPath = subPaths[0];
-		tmpShape = new THREE.Shape();
-		tmpShape.actions = tmpPath.actions;
-		tmpShape.curves = tmpPath.curves;
-		shapes.push( tmpShape );
-		return shapes;
+	return function ( force ) {
 
-	}
+		THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
 
-	var holesFirst = ! THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() );
-	holesFirst = isCCW ? ! holesFirst : holesFirst;
+		position.setFromMatrixPosition( this.matrixWorld );
 
-	// console.log("Holes first", holesFirst);
-	
-	var betterShapeHoles = [];
-	var newShapes = [];
-	var newShapeHoles = [];
-	var mainIdx = 0;
-	var tmpPoints;
+		this.panner.setPosition( position.x, position.y, position.z );
 
-	newShapes[mainIdx] = undefined;
-	newShapeHoles[mainIdx] = [];
+	};
 
-	var i, il;
+} )();
 
-	for ( i = 0, il = subPaths.length; i < il; i ++ ) {
+// File:src/extras/audio/AudioListener.js
 
-		tmpPath = subPaths[ i ];
-		tmpPoints = tmpPath.getPoints();
-		solid = THREE.Shape.Utils.isClockWise( tmpPoints );
-		solid = isCCW ? ! solid : solid;
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-		if ( solid ) {
+THREE.AudioListener = function () {
 
-			if ( (! holesFirst ) && ( newShapes[mainIdx] ) )	mainIdx ++;
+	THREE.Object3D.call( this );
 
-			newShapes[mainIdx] = { s: new THREE.Shape(), p: tmpPoints };
-			newShapes[mainIdx].s.actions = tmpPath.actions;
-			newShapes[mainIdx].s.curves = tmpPath.curves;
-			
-			if ( holesFirst )	mainIdx ++;
-			newShapeHoles[mainIdx] = [];
+	this.type = 'AudioListener';
 
-			//console.log('cw', i);
+	this.context = new ( window.AudioContext || window.webkitAudioContext )();
 
-		} else {
+};
 
-			newShapeHoles[mainIdx].push( { h: tmpPath, p: tmpPoints[0] } );
+THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype );
 
-			//console.log('ccw', i);
+THREE.AudioListener.prototype.updateMatrixWorld = ( function () {
 
-		}
+	var position = new THREE.Vector3();
+	var quaternion = new THREE.Quaternion();
+	var scale = new THREE.Vector3();
 
-	}
+	var orientation = new THREE.Vector3();
+	var velocity = new THREE.Vector3();
 
-	// only Holes? -> probably all Shapes with wrong orientation
-	if ( ! newShapes[0] )	return	toShapesNoHoles( subPaths );
+	var positionPrev = new THREE.Vector3();
 
+	return function ( force ) {
 
-	if ( newShapes.length > 1 ) {
-		var ambigious = false;
-		var toChange = [];
+		THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
 
-		for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
-			betterShapeHoles[sIdx] = [];
-		}
-		for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
-			var sh = newShapes[sIdx];
-			var sho = newShapeHoles[sIdx];
-			for (var hIdx = 0; hIdx < sho.length; hIdx ++ ) {
-				var ho = sho[hIdx];
-				var hole_unassigned = true;
-				for (var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {
-					if ( isPointInsidePolygon( ho.p, newShapes[s2Idx].p ) ) {
-						if ( sIdx != s2Idx )		toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );
-						if ( hole_unassigned ) {
-							hole_unassigned = false;
-							betterShapeHoles[s2Idx].push( ho );
-						} else {
-							ambigious = true;
-						}
-					}
-				}
-				if ( hole_unassigned ) { betterShapeHoles[sIdx].push( ho ); }
-			}
-		}
-		// console.log("ambigious: ", ambigious);
-		if ( toChange.length > 0 ) {
-			// console.log("to change: ", toChange);
-			if (! ambigious)	newShapeHoles = betterShapeHoles;
-		}
-	}
+		var listener = this.context.listener;
 
-	var tmpHoles, j, jl;
-	for ( i = 0, il = newShapes.length; i < il; i ++ ) {
-		tmpShape = newShapes[i].s;
-		shapes.push( tmpShape );
-		tmpHoles = newShapeHoles[i];
-		for ( j = 0, jl = tmpHoles.length; j < jl; j ++ ) {
-			tmpShape.holes.push( tmpHoles[j].h );
-		}
-	}
+		this.matrixWorld.decompose( position, quaternion, scale );
 
-	//console.log("shape", shapes);
+		orientation.set( 0, 0, -1 ).applyQuaternion( quaternion );
+		velocity.subVectors( position, positionPrev );
 
-	return shapes;
+		listener.setPosition( position.x, position.y, position.z );
+		listener.setOrientation( orientation.x, orientation.y, orientation.z, this.up.x, this.up.y, this.up.z );
+		listener.setVelocity( velocity.x, velocity.y, velocity.z );
 
-};
+		positionPrev.copy( position );
 
-// File:src/extras/core/Shape.js
+	};
+
+} )();
+
+// File:src/extras/core/Curve.js
 
 /**
  * @author zz85 / http://www.lab4games.net/zz85/blog
- * Defines a 2d shape plane using paths.
+ * Extensible curve object
+ *
+ * Some common of Curve methods
+ * .getPoint(t), getTangent(t)
+ * .getPointAt(u), getTagentAt(u)
+ * .getPoints(), .getSpacedPoints()
+ * .getLength()
+ * .updateArcLengths()
+ *
+ * This following classes subclasses THREE.Curve:
+ *
+ * -- 2d classes --
+ * THREE.LineCurve
+ * THREE.QuadraticBezierCurve
+ * THREE.CubicBezierCurve
+ * THREE.SplineCurve
+ * THREE.ArcCurve
+ * THREE.EllipseCurve
+ *
+ * -- 3d classes --
+ * THREE.LineCurve3
+ * THREE.QuadraticBezierCurve3
+ * THREE.CubicBezierCurve3
+ * THREE.SplineCurve3
+ * THREE.ClosedSplineCurve3
+ *
+ * A series of curves can be represented as a THREE.CurvePath
+ *
  **/
 
-// STEP 1 Create a path.
-// STEP 2 Turn path into shape.
-// STEP 3 ExtrudeGeometry takes in Shape/Shapes
-// STEP 3a - Extract points from each shape, turn to vertices
-// STEP 3b - Triangulate each shape, add faces.
-
-THREE.Shape = function () {
+/**************************************************************
+ *	Abstract Curve base class
+ **************************************************************/
 
-	THREE.Path.apply( this, arguments );
-	this.holes = [];
+THREE.Curve = function () {
 
 };
 
-THREE.Shape.prototype = Object.create( THREE.Path.prototype );
-
-// Convenience method to return ExtrudeGeometry
+// Virtual base class method to overwrite and implement in subclasses
+//	- t [0 .. 1]
 
-THREE.Shape.prototype.extrude = function ( options ) {
+THREE.Curve.prototype.getPoint = function ( t ) {
 
-	var extruded = new THREE.ExtrudeGeometry( this, options );
-	return extruded;
+	console.log( "Warning, getPoint() not implemented!" );
+	return null;
 
 };
 
-// Convenience method to return ShapeGeometry
+// Get point at relative position in curve according to arc length
+// - u [0 .. 1]
 
-THREE.Shape.prototype.makeGeometry = function ( options ) {
+THREE.Curve.prototype.getPointAt = function ( u ) {
 
-	var geometry = new THREE.ShapeGeometry( this, options );
-	return geometry;
+	var t = this.getUtoTmapping( u );
+	return this.getPoint( t );
 
 };
 
-// Get points of holes
+// Get sequence of points using getPoint( t )
 
-THREE.Shape.prototype.getPointsHoles = function ( divisions ) {
+THREE.Curve.prototype.getPoints = function ( divisions ) {
 
-	var i, il = this.holes.length, holesPts = [];
+	if ( ! divisions ) divisions = 5;
 
-	for ( i = 0; i < il; i ++ ) {
+	var d, pts = [];
 
-		holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends );
+	for ( d = 0; d <= divisions; d ++ ) {
+
+		pts.push( this.getPoint( d / divisions ) );
 
 	}
 
-	return holesPts;
+	return pts;
 
 };
 
-// Get points of holes (spaced by regular distance)
+// Get sequence of points using getPointAt( u )
 
-THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) {
+THREE.Curve.prototype.getSpacedPoints = function ( divisions ) {
 
-	var i, il = this.holes.length, holesPts = [];
+	if ( ! divisions ) divisions = 5;
 
-	for ( i = 0; i < il; i ++ ) {
+	var d, pts = [];
 
-		holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends );
+	for ( d = 0; d <= divisions; d ++ ) {
+
+		pts.push( this.getPointAt( d / divisions ) );
 
 	}
 
-	return holesPts;
+	return pts;
 
 };
 
+// Get total curve arc length
 
-// Get points of shape and holes (keypoints based on segments parameter)
+THREE.Curve.prototype.getLength = function () {
 
-THREE.Shape.prototype.extractAllPoints = function ( divisions ) {
+	var lengths = this.getLengths();
+	return lengths[ lengths.length - 1 ];
 
-	return {
+};
 
-		shape: this.getTransformedPoints( divisions ),
-		holes: this.getPointsHoles( divisions )
+// Get list of cumulative segment lengths
 
-	};
+THREE.Curve.prototype.getLengths = function ( divisions ) {
 
-};
+	if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200;
 
-THREE.Shape.prototype.extractPoints = function ( divisions ) {
+	if ( this.cacheArcLengths
+		&& ( this.cacheArcLengths.length == divisions + 1 )
+		&& ! this.needsUpdate) {
+
+		//console.log( "cached", this.cacheArcLengths );
+		return this.cacheArcLengths;
 
-	if (this.useSpacedPoints) {
-		return this.extractAllSpacedPoints(divisions);
 	}
 
-	return this.extractAllPoints(divisions);
+	this.needsUpdate = false;
 
-};
+	var cache = [];
+	var current, last = this.getPoint( 0 );
+	var p, sum = 0;
 
-//
-// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) {
-//
-// 	return {
-//
-// 		shape: this.transform( bend, divisions ),
-// 		holes: this.getPointsHoles( divisions, bend )
-//
-// 	};
-//
-// };
+	cache.push( 0 );
 
-// Get points of shape and holes (spaced by regular distance)
+	for ( p = 1; p <= divisions; p ++ ) {
 
-THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) {
+		current = this.getPoint ( p / divisions );
+		sum += current.distanceTo( last );
+		cache.push( sum );
+		last = current;
 
-	return {
+	}
 
-		shape: this.getTransformedSpacedPoints( divisions ),
-		holes: this.getSpacedPointsHoles( divisions )
+	this.cacheArcLengths = cache;
 
-	};
+	return cache; // { sums: cache, sum:sum }; Sum is in the last element.
 
 };
 
-/**************************************************************
- *	Utils
- **************************************************************/
 
-THREE.Shape.Utils = {
+THREE.Curve.prototype.updateArcLengths = function() {
+	this.needsUpdate = true;
+	this.getLengths();
+};
 
-	triangulateShape: function ( contour, holes ) {
+// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance
 
-		function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) {
-			// inOtherPt needs to be colinear to the inSegment
-			if ( inSegPt1.x != inSegPt2.x ) {
-				if ( inSegPt1.x < inSegPt2.x ) {
-					return	( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) );
-				} else {
-					return	( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) );
-				}
-			} else {
-				if ( inSegPt1.y < inSegPt2.y ) {
-					return	( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) );
-				} else {
-					return	( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) );
-				}
-			}
-		}
+THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) {
 
-		function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) {
-			var EPSILON = 0.0000000001;
+	var arcLengths = this.getLengths();
 
-			var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x,   seg1dy = inSeg1Pt2.y - inSeg1Pt1.y;
-			var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x,   seg2dy = inSeg2Pt2.y - inSeg2Pt1.y;
+	var i = 0, il = arcLengths.length;
 
-			var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x;
-			var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y;
+	var targetArcLength; // The targeted u distance value to get
 
-			var limit		= seg1dy * seg2dx - seg1dx * seg2dy;
-			var perpSeg1	= seg1dy * seg1seg2dx - seg1dx * seg1seg2dy;
+	if ( distance ) {
 
-			if ( Math.abs(limit) > EPSILON ) {			// not parallel
+		targetArcLength = distance;
 
-				var perpSeg2;
-				if ( limit > 0 ) {
-					if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) 		return [];
-					perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
-					if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) 		return [];
-				} else {
-					if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) 		return [];
-					perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
-					if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) 		return [];
-				}
+	} else {
 
-				// i.e. to reduce rounding errors
-				// intersection at endpoint of segment#1?
-				if ( perpSeg2 == 0 ) {
-					if ( ( inExcludeAdjacentSegs ) &&
-						 ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) )		return [];
-					return  [ inSeg1Pt1 ];
-				}
-				if ( perpSeg2 == limit ) {
-					if ( ( inExcludeAdjacentSegs ) &&
-						 ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) )		return [];
-					return  [ inSeg1Pt2 ];
-				}
-				// intersection at endpoint of segment#2?
-				if ( perpSeg1 == 0 )		return  [ inSeg2Pt1 ];
-				if ( perpSeg1 == limit )	return  [ inSeg2Pt2 ];
+		targetArcLength = u * arcLengths[ il - 1 ];
 
-				// return real intersection point
-				var factorSeg1 = perpSeg2 / limit;
-				return	[ { x: inSeg1Pt1.x + factorSeg1 * seg1dx,
-							y: inSeg1Pt1.y + factorSeg1 * seg1dy } ];
+	}
 
-			} else {		// parallel or colinear
-				if ( ( perpSeg1 != 0 ) ||
-					 ( seg2dy * seg1seg2dx != seg2dx * seg1seg2dy ) ) 			return [];
+	//var time = Date.now();
 
-				// they are collinear or degenerate
-				var seg1Pt = ( (seg1dx == 0) && (seg1dy == 0) );	// segment1 ist just a point?
-				var seg2Pt = ( (seg2dx == 0) && (seg2dy == 0) );	// segment2 ist just a point?
-				// both segments are points
-				if ( seg1Pt && seg2Pt ) {
-					if ( (inSeg1Pt1.x != inSeg2Pt1.x) ||
-						 (inSeg1Pt1.y != inSeg2Pt1.y) )		return [];   	// they are distinct  points
-					return  [ inSeg1Pt1 ];                 					// they are the same point
-				}
-				// segment#1  is a single point
-				if ( seg1Pt ) {
-					if (! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) )		return [];		// but not in segment#2
-					return  [ inSeg1Pt1 ];
-				}
-				// segment#2  is a single point
-				if ( seg2Pt ) {
-					if (! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) )		return [];		// but not in segment#1
-					return  [ inSeg2Pt1 ];
-				}
+	// binary search for the index with largest value smaller than target u distance
 
-				// they are collinear segments, which might overlap
-				var seg1min, seg1max, seg1minVal, seg1maxVal;
-				var seg2min, seg2max, seg2minVal, seg2maxVal;
-				if (seg1dx != 0) {		// the segments are NOT on a vertical line
-					if ( inSeg1Pt1.x < inSeg1Pt2.x ) {
-						seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x;
-						seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x;
-					} else {
-						seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x;
-						seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x;
-					}
-					if ( inSeg2Pt1.x < inSeg2Pt2.x ) {
-						seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x;
-						seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x;
-					} else {
-						seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x;
-						seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x;
-					}
-				} else {				// the segments are on a vertical line
-					if ( inSeg1Pt1.y < inSeg1Pt2.y ) {
-						seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y;
-						seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y;
-					} else {
-						seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y;
-						seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y;
-					}
-					if ( inSeg2Pt1.y < inSeg2Pt2.y ) {
-						seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y;
-						seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y;
-					} else {
-						seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y;
-						seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y;
-					}
-				}
-				if ( seg1minVal <= seg2minVal ) {
-					if ( seg1maxVal <  seg2minVal )	return [];
-					if ( seg1maxVal == seg2minVal )	{
-						if ( inExcludeAdjacentSegs )		return [];
-						return [ seg2min ];
-					}
-					if ( seg1maxVal <= seg2maxVal )	return [ seg2min, seg1max ];
-					return	[ seg2min, seg2max ];
-				} else {
-					if ( seg1minVal >  seg2maxVal )	return [];
-					if ( seg1minVal == seg2maxVal )	{
-						if ( inExcludeAdjacentSegs )		return [];
-						return [ seg1min ];
-					}
-					if ( seg1maxVal <= seg2maxVal )	return [ seg1min, seg1max ];
-					return	[ seg1min, seg2max ];
-				}
-			}
-		}
+	var low = 0, high = il - 1, comparison;
 
-		function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) {
-			// The order of legs is important
+	while ( low <= high ) {
 
-			var EPSILON = 0.0000000001;
+		i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
 
-			// translation of all points, so that Vertex is at (0,0)
-			var legFromPtX	= inLegFromPt.x - inVertex.x,  legFromPtY	= inLegFromPt.y - inVertex.y;
-			var legToPtX	= inLegToPt.x	- inVertex.x,  legToPtY		= inLegToPt.y	- inVertex.y;
-			var otherPtX	= inOtherPt.x	- inVertex.x,  otherPtY		= inOtherPt.y	- inVertex.y;
+		comparison = arcLengths[ i ] - targetArcLength;
 
-			// main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg.
-			var from2toAngle	= legFromPtX * legToPtY - legFromPtY * legToPtX;
-			var from2otherAngle	= legFromPtX * otherPtY - legFromPtY * otherPtX;
+		if ( comparison < 0 ) {
 
-			if ( Math.abs(from2toAngle) > EPSILON ) {			// angle != 180 deg.
+			low = i + 1;
+			continue;
 
-				var other2toAngle		= otherPtX * legToPtY - otherPtY * legToPtX;
-				// console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle );
+		} else if ( comparison > 0 ) {
 
-				if ( from2toAngle > 0 ) {				// main angle < 180 deg.
-					return	( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) );
-				} else {								// main angle > 180 deg.
-					return	( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) );
-				}
-			} else {										// angle == 180 deg.
-				// console.log( "from2to: 180 deg., from2other: " + from2otherAngle  );
-				return	( from2otherAngle > 0 );
-			}
-		}
+			high = i - 1;
+			continue;
 
+		} else {
 
-		function removeHoles( contour, holes ) {
+			high = i;
+			break;
 
-			var shape = contour.concat(); // work on this shape
-			var hole;
+			// DONE
 
-			function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) {
-				// Check if hole point lies within angle around shape point
-				var lastShapeIdx = shape.length - 1;
+		}
 
-				var prevShapeIdx = inShapeIdx - 1;
-				if ( prevShapeIdx < 0 )			prevShapeIdx = lastShapeIdx;
+	}
 
-				var nextShapeIdx = inShapeIdx + 1;
-				if ( nextShapeIdx > lastShapeIdx )	nextShapeIdx = 0;
+	i = high;
 
-				var insideAngle = isPointInsideAngle( shape[inShapeIdx], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[inHoleIdx] );
-				if (! insideAngle ) {
-					// console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y );
-					return	false;
-				}
+	//console.log('b' , i, low, high, Date.now()- time);
 
-				// Check if shape point lies within angle around hole point
-				var lastHoleIdx = hole.length - 1;
+	if ( arcLengths[ i ] == targetArcLength ) {
 
-				var prevHoleIdx = inHoleIdx - 1;
-				if ( prevHoleIdx < 0 )			prevHoleIdx = lastHoleIdx;
+		var t = i / ( il - 1 );
+		return t;
 
-				var nextHoleIdx = inHoleIdx + 1;
-				if ( nextHoleIdx > lastHoleIdx )	nextHoleIdx = 0;
+	}
 
-				insideAngle = isPointInsideAngle( hole[inHoleIdx], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[inShapeIdx] );
-				if (! insideAngle ) {
-					// console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y );
-					return	false;
-				}
+	// we could get finer grain at lengths, or use simple interpolatation between two points
 
-				return	true;
-			}
+	var lengthBefore = arcLengths[ i ];
+    var lengthAfter = arcLengths[ i + 1 ];
 
-			function intersectsShapeEdge( inShapePt, inHolePt ) {
-				// checks for intersections with shape edges
-				var sIdx, nextIdx, intersection;
-				for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) {
-					nextIdx = sIdx+1; nextIdx %= shape.length;
-					intersection = intersect_segments_2D( inShapePt, inHolePt, shape[sIdx], shape[nextIdx], true );
-					if ( intersection.length > 0 )		return	true;
-				}
+    var segmentLength = lengthAfter - lengthBefore;
 
-				return	false;
-			}
+    // determine where we are between the 'before' and 'after' points
 
-			var indepHoles = [];
+    var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
 
-			function intersectsHoleEdge( inShapePt, inHolePt ) {
-				// checks for intersections with hole edges
-				var ihIdx, chkHole,
-					hIdx, nextIdx, intersection;
-				for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) {
-					chkHole = holes[indepHoles[ihIdx]];
-					for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) {
-						nextIdx = hIdx+1; nextIdx %= chkHole.length;
-						intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[hIdx], chkHole[nextIdx], true );
-						if ( intersection.length > 0 )		return	true;
-					}
-				}
-				return	false;
-			}
+    // add that fractional amount to t
 
-			var holeIndex, shapeIndex,
-				shapePt, holePt,
-				holeIdx, cutKey, failedCuts = [],
-				tmpShape1, tmpShape2,
-				tmpHole1, tmpHole2;
+    var t = ( i + segmentFraction ) / ( il -1 );
 
-			for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
+	return t;
 
-				indepHoles.push( h );
+};
 
-			}
+// Returns a unit vector tangent at t
+// In case any sub curve does not implement its tangent derivation,
+// 2 points a small delta apart will be used to find its gradient
+// which seems to give a reasonable approximation
 
-			var minShapeIndex = 0;
-			var counter = indepHoles.length * 2;
-			while ( indepHoles.length > 0 ) {
-				counter --;
-				if ( counter < 0 ) {
-					console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" );
-					break;
-				}
+THREE.Curve.prototype.getTangent = function( t ) {
 
-				// search for shape-vertex and hole-vertex,
-				// which can be connected without intersections
-				for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) {
+	var delta = 0.0001;
+	var t1 = t - delta;
+	var t2 = t + delta;
 
-					shapePt = shape[ shapeIndex ];
-					holeIndex	= - 1;
+	// Capping in case of danger
 
-					// search for hole which can be reached without intersections
-					for ( var h = 0; h < indepHoles.length; h ++ ) {
-						holeIdx = indepHoles[h];
+	if ( t1 < 0 ) t1 = 0;
+	if ( t2 > 1 ) t2 = 1;
 
-						// prevent multiple checks
-						cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx;
-						if ( failedCuts[cutKey] !== undefined )			continue;
+	var pt1 = this.getPoint( t1 );
+	var pt2 = this.getPoint( t2 );
 
-						hole = holes[holeIdx];
-						for ( var h2 = 0; h2 < hole.length; h2 ++ ) {
-							holePt = hole[ h2 ];
-							if (! isCutLineInsideAngles( shapeIndex, h2 ) )		continue;
-							if ( intersectsShapeEdge( shapePt, holePt ) )		continue;
-							if ( intersectsHoleEdge( shapePt, holePt ) )		continue;
+	var vec = pt2.clone().sub(pt1);
+	return vec.normalize();
 
-							holeIndex = h2;
-							indepHoles.splice(h,1);
+};
 
-							tmpShape1 = shape.slice( 0, shapeIndex+1 );
-							tmpShape2 = shape.slice( shapeIndex );
-							tmpHole1 = hole.slice( holeIndex );
-							tmpHole2 = hole.slice( 0, holeIndex+1 );
 
-							shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
+THREE.Curve.prototype.getTangentAt = function ( u ) {
 
-							minShapeIndex = shapeIndex;
+	var t = this.getUtoTmapping( u );
+	return this.getTangent( t );
 
-							// Debug only, to show the selected cuts
-							// glob_CutLines.push( [ shapePt, holePt ] );
+};
 
-							break;
-						}
-						if ( holeIndex >= 0 )	break;		// hole-vertex found
 
-						failedCuts[cutKey] = true;			// remember failure
-					}
-					if ( holeIndex >= 0 )	break;		// hole-vertex found
-				}
-			}
 
-			return shape; 			/* shape with no holes */
-		}
 
 
-		var i, il, f, face,
-			key, index,
-			allPointsMap = {};
-
-		// To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first.
+/**************************************************************
+ *	Utils
+ **************************************************************/
 
-		var allpoints = contour.concat();
+THREE.Curve.Utils = {
 
-		for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
+	tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
 
-			Array.prototype.push.apply( allpoints, holes[h] );
+		return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
 
-		}
+	},
 
-		//console.log( "allpoints",allpoints, allpoints.length );
+	// Puay Bing, thanks for helping with this derivative!
 
-		// prepare all points map
+	tangentCubicBezier: function (t, p0, p1, p2, p3 ) {
 
-		for ( i = 0, il = allpoints.length; i < il; i ++ ) {
+		return - 3 * p0 * (1 - t) * (1 - t)  +
+			3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) +
+			6 * t *  p2 * (1-t) - 3 * t * t * p2 +
+			3 * t * t * p3;
 
-			key = allpoints[ i ].x + ":" + allpoints[ i ].y;
+	},
 
-			if ( allPointsMap[ key ] !== undefined ) {
+	tangentSpline: function ( t, p0, p1, p2, p3 ) {
 
-				console.log( "Duplicate point", key );
+		// To check if my formulas are correct
 
-			}
+		var h00 = 6 * t * t - 6 * t; 	// derived from 2t^3 − 3t^2 + 1
+		var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t
+		var h01 = - 6 * t * t + 6 * t; 	// − 2t3 + 3t2
+		var h11 = 3 * t * t - 2 * t;	// t3 − t2
 
-			allPointsMap[ key ] = i;
+		return h00 + h10 + h01 + h11;
 
-		}
+	},
 
-		// remove holes by cutting paths to holes and adding them to the shape
-		var shapeWithoutHoles = removeHoles( contour, holes );
+	// Catmull-Rom
 
-		var triangles = THREE.FontUtils.Triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape
-		//console.log( "triangles",triangles, triangles.length );
+	interpolate: function( p0, p1, p2, p3, t ) {
 
-		// check all face vertices against all points map
+		var v0 = ( p2 - p0 ) * 0.5;
+		var v1 = ( p3 - p1 ) * 0.5;
+		var t2 = t * t;
+		var t3 = t * t2;
+		return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
 
-		for ( i = 0, il = triangles.length; i < il; i ++ ) {
+	}
 
-			face = triangles[ i ];
+};
 
-			for ( f = 0; f < 3; f ++ ) {
 
-				key = face[ f ].x + ":" + face[ f ].y;
+// TODO: Transformation for Curves?
 
-				index = allPointsMap[ key ];
+/**************************************************************
+ *	3D Curves
+ **************************************************************/
 
-				if ( index !== undefined ) {
+// A Factory method for creating new curve subclasses
 
-					face[ f ] = index;
+THREE.Curve.create = function ( constructor, getPointFunc ) {
 
-				}
+	constructor.prototype = Object.create( THREE.Curve.prototype );
+	constructor.prototype.getPoint = getPointFunc;
 
-			}
+	return constructor;
 
-		}
+};
 
-		return triangles.concat();
+// File:src/extras/core/CurvePath.js
 
-	},
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ *
+ **/
 
-	isClockWise: function ( pts ) {
+/**************************************************************
+ *	Curved Path - a curve path is simply a array of connected
+ *  curves, but retains the api of a curve
+ **************************************************************/
 
-		return THREE.FontUtils.Triangulate.area( pts ) < 0;
+THREE.CurvePath = function () {
 
-	},
+	this.curves = [];
+	this.bends = [];
+	
+	this.autoClose = false; // Automatically closes the path
+};
 
-	// Bezier Curves formulas obtained from
-	// http://en.wikipedia.org/wiki/B%C3%A9zier_curve
+THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype );
 
-	// Quad Bezier Functions
+THREE.CurvePath.prototype.add = function ( curve ) {
 
-	b2p0: function ( t, p ) {
+	this.curves.push( curve );
 
-		var k = 1 - t;
-		return k * k * p;
+};
 
-	},
+THREE.CurvePath.prototype.checkConnection = function() {
+	// TODO
+	// If the ending of curve is not connected to the starting
+	// or the next curve, then, this is not a real path
+};
 
-	b2p1: function ( t, p ) {
+THREE.CurvePath.prototype.closePath = function() {
+	// TODO Test
+	// and verify for vector3 (needs to implement equals)
+	// Add a line curve if start and end of lines are not connected
+	var startPoint = this.curves[0].getPoint(0);
+	var endPoint = this.curves[this.curves.length-1].getPoint(1);
+	
+	if (! startPoint.equals(endPoint)) {
+		this.curves.push( new THREE.LineCurve(endPoint, startPoint) );
+	}
+	
+};
 
-		return 2 * ( 1 - t ) * t * p;
+// To get accurate point with reference to
+// entire path distance at time t,
+// following has to be done:
 
-	},
+// 1. Length of each sub path have to be known
+// 2. Locate and identify type of curve
+// 3. Get t for the curve
+// 4. Return curve.getPointAt(t')
 
-	b2p2: function ( t, p ) {
+THREE.CurvePath.prototype.getPoint = function( t ) {
 
-		return t * t * p;
+	var d = t * this.getLength();
+	var curveLengths = this.getCurveLengths();
+	var i = 0, diff, curve;
 
-	},
+	// To think about boundaries points.
 
-	b2: function ( t, p0, p1, p2 ) {
+	while ( i < curveLengths.length ) {
 
-		return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 );
+		if ( curveLengths[ i ] >= d ) {
 
-	},
+			diff = curveLengths[ i ] - d;
+			curve = this.curves[ i ];
 
-	// Cubic Bezier Functions
+			var u = 1 - diff / curve.getLength();
 
-	b3p0: function ( t, p ) {
+			return curve.getPointAt( u );
 
-		var k = 1 - t;
-		return k * k * k * p;
+			break;
+		}
 
-	},
+		i ++;
 
-	b3p1: function ( t, p ) {
+	}
 
-		var k = 1 - t;
-		return 3 * k * k * t * p;
+	return null;
 
-	},
+	// loop where sum != 0, sum > d , sum+1 <d
 
-	b3p2: function ( t, p ) {
+};
 
-		var k = 1 - t;
-		return 3 * k * t * t * p;
+/*
+THREE.CurvePath.prototype.getTangent = function( t ) {
+};*/
 
-	},
 
-	b3p3: function ( t, p ) {
+// We cannot use the default THREE.Curve getPoint() with getLength() because in
+// THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath
+// getPoint() depends on getLength
 
-		return t * t * t * p;
+THREE.CurvePath.prototype.getLength = function() {
 
-	},
+	var lens = this.getCurveLengths();
+	return lens[ lens.length - 1 ];
 
-	b3: function ( t, p0, p1, p2, p3 ) {
+};
 
-		return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) +  this.b3p3( t, p3 );
+// Compute lengths and cache them
+// We cannot overwrite getLengths() because UtoT mapping uses it.
 
-	}
+THREE.CurvePath.prototype.getCurveLengths = function() {
 
-};
+	// We use cache values if curves and cache array are same length
 
+	if ( this.cacheLengths && this.cacheLengths.length == this.curves.length ) {
 
-// File:src/extras/curves/LineCurve.js
+		return this.cacheLengths;
 
-/**************************************************************
- *	Line
- **************************************************************/
+	};
 
-THREE.LineCurve = function ( v1, v2 ) {
+	// Get length of subsurve
+	// Push sums into cached array
 
-	this.v1 = v1;
-	this.v2 = v2;
+	var lengths = [], sums = 0;
+	var i, il = this.curves.length;
 
-};
+	for ( i = 0; i < il; i ++ ) {
 
-THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype );
+		sums += this.curves[ i ].getLength();
+		lengths.push( sums );
 
-THREE.LineCurve.prototype.getPoint = function ( t ) {
+	}
 
-	var point = this.v2.clone().sub(this.v1);
-	point.multiplyScalar( t ).add( this.v1 );
+	this.cacheLengths = lengths;
 
-	return point;
+	return lengths;
 
 };
 
-// Line curve is linear, so we can overwrite default getPointAt
 
-THREE.LineCurve.prototype.getPointAt = function ( u ) {
 
-	return this.getPoint( u );
+// Returns min and max coordinates
 
-};
+THREE.CurvePath.prototype.getBoundingBox = function () {
 
-THREE.LineCurve.prototype.getTangent = function( t ) {
+	var points = this.getPoints();
 
-	var tangent = this.v2.clone().sub(this.v1);
+	var maxX, maxY, maxZ;
+	var minX, minY, minZ;
 
-	return tangent.normalize();
+	maxX = maxY = Number.NEGATIVE_INFINITY;
+	minX = minY = Number.POSITIVE_INFINITY;
 
-};
+	var p, i, il, sum;
 
-// File:src/extras/curves/QuadraticBezierCurve.js
+	var v3 = points[0] instanceof THREE.Vector3;
 
-/**************************************************************
- *	Quadratic Bezier curve
- **************************************************************/
+	sum = v3 ? new THREE.Vector3() : new THREE.Vector2();
 
+	for ( i = 0, il = points.length; i < il; i ++ ) {
 
-THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) {
+		p = points[ i ];
 
-	this.v0 = v0;
-	this.v1 = v1;
-	this.v2 = v2;
+		if ( p.x > maxX ) maxX = p.x;
+		else if ( p.x < minX ) minX = p.x;
 
-};
+		if ( p.y > maxY ) maxY = p.y;
+		else if ( p.y < minY ) minY = p.y;
 
-THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype );
+		if ( v3 ) {
 
+			if ( p.z > maxZ ) maxZ = p.z;
+			else if ( p.z < minZ ) minZ = p.z;
 
-THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) {
+		}
 
-	var vector = new THREE.Vector2();
+		sum.add( p );
 
-	vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
-	vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
-
-	return vector;
+	}
 
-};
+	var ret = {
 
+		minX: minX,
+		minY: minY,
+		maxX: maxX,
+		maxY: maxY
 
-THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {
+	};
 
-	var vector = new THREE.Vector2();
+	if ( v3 ) {
 
-	vector.x = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x );
-	vector.y = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y );
+		ret.maxZ = maxZ;
+		ret.minZ = minZ;
 
-	// returns unit vector
+	}
 
-	return vector.normalize();
+	return ret;
 
 };
 
-// File:src/extras/curves/CubicBezierCurve.js
-
 /**************************************************************
- *	Cubic Bezier curve
+ *	Create Geometries Helpers
  **************************************************************/
 
-THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) {
-
-	this.v0 = v0;
-	this.v1 = v1;
-	this.v2 = v2;
-	this.v3 = v3;
+/// Generate geometry from path points (for Line or Points objects)
 
-};
+THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) {
 
-THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype );
+	var pts = this.getPoints( divisions, true );
+	return this.createGeometry( pts );
 
-THREE.CubicBezierCurve.prototype.getPoint = function ( t ) {
+};
 
-	var tx, ty;
+// Generate geometry from equidistance sampling along the path
 
-	tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
-	ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
+THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) {
 
-	return new THREE.Vector2( tx, ty );
+	var pts = this.getSpacedPoints( divisions, true );
+	return this.createGeometry( pts );
 
 };
 
-THREE.CubicBezierCurve.prototype.getTangent = function( t ) {
+THREE.CurvePath.prototype.createGeometry = function( points ) {
 
-	var tx, ty;
+	var geometry = new THREE.Geometry();
 
-	tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
-	ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
+	for ( var i = 0; i < points.length; i ++ ) {
 
-	var tangent = new THREE.Vector2( tx, ty );
-	tangent.normalize();
+		geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) );
 
-	return tangent;
+	}
+
+	return geometry;
 
 };
 
-// File:src/extras/curves/SplineCurve.js
 
 /**************************************************************
- *	Spline curve
+ *	Bend / Wrap Helper Methods
  **************************************************************/
 
-THREE.SplineCurve = function ( points /* array of Vector2 */ ) {
+// Wrap path / Bend modifiers?
 
-	this.points = ( points == undefined ) ? [] : points;
+THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) {
+
+	this.bends.push( bendpath );
 
 };
 
-THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype );
+THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) {
 
-THREE.SplineCurve.prototype.getPoint = function ( t ) {
+	var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints
+	var i, il;
 
-	var points = this.points;
-	var point = ( points.length - 1 ) * t;
+	if ( ! bends ) {
 
-	var intPoint = Math.floor( point );
-	var weight = point - intPoint;
+		bends = this.bends;
 
-	var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ]
-	var point1 = points[ intPoint ]
-	var point2 = points[ intPoint > points.length - 2 ? points.length -1 : intPoint + 1 ]
-	var point3 = points[ intPoint > points.length - 3 ? points.length -1 : intPoint + 2 ]
+	}
 
-	var vector = new THREE.Vector2();
+	for ( i = 0, il = bends.length; i < il; i ++ ) {
 
-	vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight );
-	vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight );
+		oldPts = this.getWrapPoints( oldPts, bends[ i ] );
 
-	return vector;
+	}
+
+	return oldPts;
 
 };
 
-// File:src/extras/curves/EllipseCurve.js
+THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) {
 
-/**************************************************************
- *	Ellipse curve
- **************************************************************/
+	var oldPts = this.getSpacedPoints( segments );
 
-THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) {
+	var i, il;
 
-	this.aX = aX;
-	this.aY = aY;
+	if ( ! bends ) {
 
-	this.xRadius = xRadius;
-	this.yRadius = yRadius;
+		bends = this.bends;
 
-	this.aStartAngle = aStartAngle;
-	this.aEndAngle = aEndAngle;
+	}
 
-	this.aClockwise = aClockwise;
+	for ( i = 0, il = bends.length; i < il; i ++ ) {
 
-};
+		oldPts = this.getWrapPoints( oldPts, bends[ i ] );
 
-THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype );
+	}
 
-THREE.EllipseCurve.prototype.getPoint = function ( t ) {
+	return oldPts;
 
-	var deltaAngle = this.aEndAngle - this.aStartAngle;
+};
 
-	if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2;
-	if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2;
+// This returns getPoints() bend/wrapped around the contour of a path.
+// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html
 
-	var angle;
+THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) {
 
-	if ( this.aClockwise === true ) {
+	var bounds = this.getBoundingBox();
 
-		angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle );
+	var i, il, p, oldX, oldY, xNorm;
 
-	} else {
+	for ( i = 0, il = oldPts.length; i < il; i ++ ) {
 
-		angle = this.aStartAngle + t * deltaAngle;
+		p = oldPts[ i ];
 
-	}
-	
-	var vector = new THREE.Vector2();
+		oldX = p.x;
+		oldY = p.y;
 
-	vector.x = this.aX + this.xRadius * Math.cos( angle );
-	vector.y = this.aY + this.yRadius * Math.sin( angle );
+		xNorm = oldX / bounds.maxX;
 
-	return vector;
+		// If using actual distance, for length > path, requires line extrusions
+		//xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance
 
-};
+		xNorm = path.getUtoTmapping( xNorm, oldX );
 
-// File:src/extras/curves/ArcCurve.js
+		// check for out of bounds?
 
-/**************************************************************
- *	Arc curve
- **************************************************************/
+		var pathPt = path.getPoint( xNorm );
+		var normal = path.getTangent( xNorm );
+		normal.set( - normal.y, normal.x ).multiplyScalar( oldY );
 
-THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
+		p.x = pathPt.x + normal.x;
+		p.y = pathPt.y + normal.y;
 
-	THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
-};
+	}
 
-THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype );
+	return oldPts;
 
-// File:src/extras/curves/LineCurve3.js
+};
 
-/**************************************************************
- *	Line3D
- **************************************************************/
 
-THREE.LineCurve3 = THREE.Curve.create(
+// File:src/extras/core/Gyroscope.js
 
-	function ( v1, v2 ) {
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-		this.v1 = v1;
-		this.v2 = v2;
+THREE.Gyroscope = function () {
 
-	},
+	THREE.Object3D.call( this );
 
-	function ( t ) {
+};
 
-		var vector = new THREE.Vector3();
+THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype );
 
-		vector.subVectors( this.v2, this.v1 ); // diff
-		vector.multiplyScalar( t );
-		vector.add( this.v1 );
+THREE.Gyroscope.prototype.updateMatrixWorld = ( function () {
 
-		return vector;
+	var translationObject = new THREE.Vector3();
+	var quaternionObject = new THREE.Quaternion();
+	var scaleObject = new THREE.Vector3();
 
-	}
+	var translationWorld = new THREE.Vector3();
+	var quaternionWorld = new THREE.Quaternion();
+	var scaleWorld = new THREE.Vector3();
 
-);
+	return function ( force ) {
 
-// File:src/extras/curves/QuadraticBezierCurve3.js
+		this.matrixAutoUpdate && this.updateMatrix();
 
-/**************************************************************
- *	Quadratic Bezier 3D curve
- **************************************************************/
+		// update matrixWorld
 
-THREE.QuadraticBezierCurve3 = THREE.Curve.create(
+		if ( this.matrixWorldNeedsUpdate || force ) {
 
-	function ( v0, v1, v2 ) {
+			if ( this.parent ) {
 
-		this.v0 = v0;
-		this.v1 = v1;
-		this.v2 = v2;
+				this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
 
-	},
+				this.matrixWorld.decompose( translationWorld, quaternionWorld, scaleWorld );
+				this.matrix.decompose( translationObject, quaternionObject, scaleObject );
 
-	function ( t ) {
+				this.matrixWorld.compose( translationWorld, quaternionObject, scaleWorld );
 
-		var vector = new THREE.Vector3();
 
-		vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
-		vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
-		vector.z = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z );
+			} else {
 
-		return vector;
+				this.matrixWorld.copy( this.matrix );
 
-	}
+			}
 
-);
 
-// File:src/extras/curves/CubicBezierCurve3.js
+			this.matrixWorldNeedsUpdate = false;
 
-/**************************************************************
- *	Cubic Bezier 3D curve
- **************************************************************/
+			force = true;
 
-THREE.CubicBezierCurve3 = THREE.Curve.create(
+		}
 
-	function ( v0, v1, v2, v3 ) {
+		// update children
 
-		this.v0 = v0;
-		this.v1 = v1;
-		this.v2 = v2;
-		this.v3 = v3;
+		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
 
-	},
+			this.children[ i ].updateMatrixWorld( force );
 
-	function ( t ) {
+		}
 
-		var vector = new THREE.Vector3();
+	};
+	
+}() );
 
-		vector.x = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
-		vector.y = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
-		vector.z = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z );
+// File:src/extras/core/Path.js
 
-		return vector;
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * Creates free form 2d path using series of points, lines or curves.
+ *
+ **/
 
-	}
+THREE.Path = function ( points ) {
 
-);
+	THREE.CurvePath.call(this);
 
-// File:src/extras/curves/SplineCurve3.js
+	this.actions = [];
 
-/**************************************************************
- *	Spline 3D curve
- **************************************************************/
+	if ( points ) {
 
+		this.fromPoints( points );
 
-THREE.SplineCurve3 = THREE.Curve.create(
+	}
 
-	function ( points /* array of Vector3 */) {
+};
 
-		this.points = ( points == undefined ) ? [] : points;
+THREE.Path.prototype = Object.create( THREE.CurvePath.prototype );
 
-	},
+THREE.PathActions = {
 
-	function ( t ) {
+	MOVE_TO: 'moveTo',
+	LINE_TO: 'lineTo',
+	QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve
+	BEZIER_CURVE_TO: 'bezierCurveTo', 		// Bezier cubic curve
+	CSPLINE_THRU: 'splineThru',				// Catmull-rom spline
+	ARC: 'arc',								// Circle
+	ELLIPSE: 'ellipse'
+};
 
-		var points = this.points;
-		var point = ( points.length - 1 ) * t;
+// TODO Clean up PATH API
 
-		var intPoint = Math.floor( point );
-		var weight = point - intPoint;
+// Create path using straight lines to connect all points
+// - vectors: array of Vector2
 
-		var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ];
-		var point1 = points[ intPoint ];
-		var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
-		var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];
+THREE.Path.prototype.fromPoints = function ( vectors ) {
 
-		var vector = new THREE.Vector3();
+	this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y );
 
-		vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight );
-		vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight );
-		vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight );
+	for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) {
 
-		return vector;
+		this.lineTo( vectors[ v ].x, vectors[ v ].y );
 
-	}
+	};
 
-);
+};
 
-// File:src/extras/curves/ClosedSplineCurve3.js
+// startPath() endPath()?
 
-/**************************************************************
- *	Closed Spline 3D curve
- **************************************************************/
+THREE.Path.prototype.moveTo = function ( x, y ) {
 
+	var args = Array.prototype.slice.call( arguments );
+	this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } );
 
-THREE.ClosedSplineCurve3 = THREE.Curve.create(
+};
 
-	function ( points /* array of Vector3 */) {
+THREE.Path.prototype.lineTo = function ( x, y ) {
 
-		this.points = ( points == undefined ) ? [] : points;
+	var args = Array.prototype.slice.call( arguments );
 
-	},
+	var lastargs = this.actions[ this.actions.length - 1 ].args;
 
-	function ( t ) {
+	var x0 = lastargs[ lastargs.length - 2 ];
+	var y0 = lastargs[ lastargs.length - 1 ];
 
-		var points = this.points;
-		var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1
+	var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) );
+	this.curves.push( curve );
 
-		var intPoint = Math.floor( point );
-		var weight = point - intPoint;
+	this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } );
 
-		intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;
+};
 
-		var point0 = points[ ( intPoint - 1 ) % points.length ];
-		var point1 = points[ ( intPoint     ) % points.length ];
-		var point2 = points[ ( intPoint + 1 ) % points.length ];
-		var point3 = points[ ( intPoint + 2 ) % points.length ];
+THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) {
 
-		var vector = new THREE.Vector3();
+	var args = Array.prototype.slice.call( arguments );
 
-		vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight );
-		vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight );
-		vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight );
+	var lastargs = this.actions[ this.actions.length - 1 ].args;
 
-		return vector;
+	var x0 = lastargs[ lastargs.length - 2 ];
+	var y0 = lastargs[ lastargs.length - 1 ];
 
-	}
+	var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ),
+												new THREE.Vector2( aCPx, aCPy ),
+												new THREE.Vector2( aX, aY ) );
+	this.curves.push( curve );
 
-);
+	this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } );
 
-// File:src/extras/animation/AnimationHandler.js
+};
 
-/**
- * @author mikael emtinger / http://gomo.se/
- */
+THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y,
+											   aCP2x, aCP2y,
+											   aX, aY ) {
 
-THREE.AnimationHandler = {
+	var args = Array.prototype.slice.call( arguments );
 
-	LINEAR: 0,
-	CATMULLROM: 1,
-	CATMULLROM_FORWARD: 2,
+	var lastargs = this.actions[ this.actions.length - 1 ].args;
 
-	//
+	var x0 = lastargs[ lastargs.length - 2 ];
+	var y0 = lastargs[ lastargs.length - 1 ];
 
-	add: function () { console.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); },
-	get: function () { console.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); },
-	remove: function () { console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); },
+	var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ),
+											new THREE.Vector2( aCP1x, aCP1y ),
+											new THREE.Vector2( aCP2x, aCP2y ),
+											new THREE.Vector2( aX, aY ) );
+	this.curves.push( curve );
 
-	//
+	this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } );
 
-	animations: [],
+};
 
-	init: function ( data ) {
+THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) {
 
-		if ( data.initialized === true ) return;
+	var args = Array.prototype.slice.call( arguments );
+	var lastargs = this.actions[ this.actions.length - 1 ].args;
 
-		// loop through all keys
+	var x0 = lastargs[ lastargs.length - 2 ];
+	var y0 = lastargs[ lastargs.length - 1 ];
+//---
+	var npts = [ new THREE.Vector2( x0, y0 ) ];
+	Array.prototype.push.apply( npts, pts );
 
-		for ( var h = 0; h < data.hierarchy.length; h ++ ) {
+	var curve = new THREE.SplineCurve( npts );
+	this.curves.push( curve );
 
-			for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
+	this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } );
 
-				// remove minus times
+};
 
-				if ( data.hierarchy[ h ].keys[ k ].time < 0 ) {
+// FUTURE: Change the API or follow canvas API?
 
-					 data.hierarchy[ h ].keys[ k ].time = 0;
+THREE.Path.prototype.arc = function ( aX, aY, aRadius,
+									  aStartAngle, aEndAngle, aClockwise ) {
 
-				}
+	var lastargs = this.actions[ this.actions.length - 1].args;
+	var x0 = lastargs[ lastargs.length - 2 ];
+	var y0 = lastargs[ lastargs.length - 1 ];
 
-				// create quaternions
+	this.absarc(aX + x0, aY + y0, aRadius,
+		aStartAngle, aEndAngle, aClockwise );
 
-				if ( data.hierarchy[ h ].keys[ k ].rot !== undefined &&
-				  ! ( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) {
+ };
 
-					var quat = data.hierarchy[ h ].keys[ k ].rot;
-					data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion().fromArray( quat );
+ THREE.Path.prototype.absarc = function ( aX, aY, aRadius,
+									  aStartAngle, aEndAngle, aClockwise ) {
+	this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise);
+ };
 
-				}
+THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius,
+									  aStartAngle, aEndAngle, aClockwise ) {
 
-			}
+	var lastargs = this.actions[ this.actions.length - 1].args;
+	var x0 = lastargs[ lastargs.length - 2 ];
+	var y0 = lastargs[ lastargs.length - 1 ];
 
-			// prepare morph target keys
+	this.absellipse(aX + x0, aY + y0, xRadius, yRadius,
+		aStartAngle, aEndAngle, aClockwise );
 
-			if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) {
+ };
 
-				// get all used
 
-				var usedMorphTargets = {};
+THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius,
+									  aStartAngle, aEndAngle, aClockwise ) {
 
-				for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
+	var args = Array.prototype.slice.call( arguments );
+	var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius,
+									aStartAngle, aEndAngle, aClockwise );
+	this.curves.push( curve );
 
-					for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
+	var lastPoint = curve.getPoint(1);
+	args.push(lastPoint.x);
+	args.push(lastPoint.y);
 
-						var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ];
-						usedMorphTargets[ morphTargetName ] = - 1;
+	this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } );
 
-					}
+ };
 
-				}
+THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) {
 
-				data.hierarchy[ h ].usedMorphTargets = usedMorphTargets;
+	if ( ! divisions ) divisions = 40;
 
+	var points = [];
 
-				// set all used on all frames
+	for ( var i = 0; i < divisions; i ++ ) {
 
-				for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
+		points.push( this.getPoint( i / divisions ) );
 
-					var influences = {};
+		//if( !this.getPoint( i / divisions ) ) throw "DIE";
 
-					for ( var morphTargetName in usedMorphTargets ) {
+	}
 
-						for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
+	// if ( closedPath ) {
+	//
+	// 	points.push( points[ 0 ] );
+	//
+	// }
 
-							if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) {
+	return points;
 
-								influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ];
-								break;
+};
 
-							}
+/* Return an array of vectors based on contour of the path */
 
-						}
+THREE.Path.prototype.getPoints = function( divisions, closedPath ) {
 
-						if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) {
+	if (this.useSpacedPoints) {
+		console.log('tata');
+		return this.getSpacedPoints( divisions, closedPath );
+	}
 
-							influences[ morphTargetName ] = 0;
+	divisions = divisions || 12;
 
-						}
+	var points = [];
 
-					}
+	var i, il, item, action, args;
+	var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0,
+		laste, j,
+		t, tx, ty;
 
-					data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences;
+	for ( i = 0, il = this.actions.length; i < il; i ++ ) {
 
-				}
+		item = this.actions[ i ];
 
-			}
+		action = item.action;
+		args = item.args;
 
+		switch( action ) {
 
-			// remove all keys that are on the same time
-
-			for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) {
-
-				if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) {
-
-					data.hierarchy[ h ].keys.splice( k, 1 );
-					k --;
-
-				}
-
-			}
+		case THREE.PathActions.MOVE_TO:
 
+			points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
 
-			// set index
+			break;
 
-			for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
+		case THREE.PathActions.LINE_TO:
 
-				data.hierarchy[ h ].keys[ k ].index = k;
+			points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
 
-			}
+			break;
 
-		}
+		case THREE.PathActions.QUADRATIC_CURVE_TO:
 
-		data.initialized = true;
+			cpx  = args[ 2 ];
+			cpy  = args[ 3 ];
 
-		return data;
+			cpx1 = args[ 0 ];
+			cpy1 = args[ 1 ];
 
-	},
+			if ( points.length > 0 ) {
 
-	parse: function ( root ) {
+				laste = points[ points.length - 1 ];
 
-		var parseRecurseHierarchy = function ( root, hierarchy ) {
+				cpx0 = laste.x;
+				cpy0 = laste.y;
 
-			hierarchy.push( root );
+			} else {
 
-			for ( var c = 0; c < root.children.length; c ++ )
-				parseRecurseHierarchy( root.children[ c ], hierarchy );
+				laste = this.actions[ i - 1 ].args;
 
-		};
+				cpx0 = laste[ laste.length - 2 ];
+				cpy0 = laste[ laste.length - 1 ];
 
-		// setup hierarchy
+			}
 
-		var hierarchy = [];
+			for ( j = 1; j <= divisions; j ++ ) {
 
-		if ( root instanceof THREE.SkinnedMesh ) {
+				t = j / divisions;
 
-			for ( var b = 0; b < root.skeleton.bones.length; b ++ ) {
+				tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
+				ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
 
-				hierarchy.push( root.skeleton.bones[ b ] );
+				points.push( new THREE.Vector2( tx, ty ) );
 
 			}
 
-		} else {
-
-			parseRecurseHierarchy( root, hierarchy );
+			break;
 
-		}
+		case THREE.PathActions.BEZIER_CURVE_TO:
 
-		return hierarchy;
+			cpx  = args[ 4 ];
+			cpy  = args[ 5 ];
 
-	},
+			cpx1 = args[ 0 ];
+			cpy1 = args[ 1 ];
 
-	play: function ( animation ) {
+			cpx2 = args[ 2 ];
+			cpy2 = args[ 3 ];
 
-		if ( this.animations.indexOf( animation ) === - 1 ) {
+			if ( points.length > 0 ) {
 
-			this.animations.push( animation );
+				laste = points[ points.length - 1 ];
 
-		}
+				cpx0 = laste.x;
+				cpy0 = laste.y;
 
-	},
+			} else {
 
-	stop: function ( animation ) {
+				laste = this.actions[ i - 1 ].args;
 
-		var index = this.animations.indexOf( animation );
+				cpx0 = laste[ laste.length - 2 ];
+				cpy0 = laste[ laste.length - 1 ];
 
-		if ( index !== - 1 ) {
+			}
 
-			this.animations.splice( index, 1 );
 
-		}
+			for ( j = 1; j <= divisions; j ++ ) {
 
-	},
+				t = j / divisions;
 
-	update: function ( deltaTimeMS ) {
+				tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
+				ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
 
-		for ( var i = 0; i < this.animations.length; i ++ ) {
+				points.push( new THREE.Vector2( tx, ty ) );
 
-			this.animations[ i ].resetBlendWeights( );
+			}
 
-		}
+			break;
 
-		for ( var i = 0; i < this.animations.length; i ++ ) {
+		case THREE.PathActions.CSPLINE_THRU:
 
-			this.animations[ i ].update( deltaTimeMS );
+			laste = this.actions[ i - 1 ].args;
 
-		}
+			var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] );
+			var spts = [ last ];
 
-	}
+			var n = divisions * args[ 0 ].length;
 
-};
+			spts = spts.concat( args[ 0 ] );
 
-// File:src/extras/animation/Animation.js
+			var spline = new THREE.SplineCurve( spts );
 
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- */
+			for ( j = 1; j <= n; j ++ ) {
 
-THREE.Animation = function ( root, data ) {
+				points.push( spline.getPointAt( j / n ) ) ;
 
-	this.root = root;
-	this.data = THREE.AnimationHandler.init( data );
-	this.hierarchy = THREE.AnimationHandler.parse( root );
+			}
 
-	this.currentTime = 0;
-	this.timeScale = 1;
+			break;
 
-	this.isPlaying = false;
-	this.loop = true;
-	this.weight = 0;
+		case THREE.PathActions.ARC:
 
-	this.interpolationType = THREE.AnimationHandler.LINEAR;
+			var aX = args[ 0 ], aY = args[ 1 ],
+				aRadius = args[ 2 ],
+				aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
+				aClockwise = !! args[ 5 ];
 
-};
+			var deltaAngle = aEndAngle - aStartAngle;
+			var angle;
+			var tdivisions = divisions * 2;
 
+			for ( j = 1; j <= tdivisions; j ++ ) {
 
-THREE.Animation.prototype.keyTypes = [ "pos", "rot", "scl" ];
+				t = j / tdivisions;
 
+				if ( ! aClockwise ) {
 
-THREE.Animation.prototype.play = function ( startTime, weight ) {
+					t = 1 - t;
 
-	this.currentTime = startTime !== undefined ? startTime : 0;
-	this.weight = weight !== undefined ? weight: 1;
+				}
 
-	this.isPlaying = true;
+				angle = aStartAngle + t * deltaAngle;
 
-	this.reset();
+				tx = aX + aRadius * Math.cos( angle );
+				ty = aY + aRadius * Math.sin( angle );
 
-	THREE.AnimationHandler.play( this );
+				//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
 
-};
+				points.push( new THREE.Vector2( tx, ty ) );
 
+			}
 
-THREE.Animation.prototype.stop = function() {
+			//console.log(points);
 
-	this.isPlaying = false;
+		  break;
+		  
+		case THREE.PathActions.ELLIPSE:
 
-	THREE.AnimationHandler.stop( this );
+			var aX = args[ 0 ], aY = args[ 1 ],
+				xRadius = args[ 2 ],
+				yRadius = args[ 3 ],
+				aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
+				aClockwise = !! args[ 6 ];
 
-};
 
-THREE.Animation.prototype.reset = function () {
+			var deltaAngle = aEndAngle - aStartAngle;
+			var angle;
+			var tdivisions = divisions * 2;
 
-	for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
+			for ( j = 1; j <= tdivisions; j ++ ) {
 
-		var object = this.hierarchy[ h ];
+				t = j / tdivisions;
 
-		object.matrixAutoUpdate = true;
+				if ( ! aClockwise ) {
 
-		if ( object.animationCache === undefined ) {
+					t = 1 - t;
 
-			object.animationCache = {
-				animations: {},
-				blending: {
-					positionWeight: 0.0,
-					quaternionWeight: 0.0,
-					scaleWeight: 0.0
 				}
-			};
-		}
 
-		if ( object.animationCache.animations[this.data.name] === undefined ) {
+				angle = aStartAngle + t * deltaAngle;
 
-			object.animationCache.animations[this.data.name] = {};
-			object.animationCache.animations[this.data.name].prevKey = { pos: 0, rot: 0, scl: 0 };
-			object.animationCache.animations[this.data.name].nextKey = { pos: 0, rot: 0, scl: 0 };
-			object.animationCache.animations[this.data.name].originalMatrix = object.matrix;
+				tx = aX + xRadius * Math.cos( angle );
+				ty = aY + yRadius * Math.sin( angle );
 
-		}
+				//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
 
-		var animationCache = object.animationCache.animations[this.data.name];
+				points.push( new THREE.Vector2( tx, ty ) );
 
-		// Get keys to match our current time
+			}
 
-		for ( var t = 0; t < 3; t ++ ) {
+			//console.log(points);
 
-			var type = this.keyTypes[ t ];
+		  break;
 
-			var prevKey = this.data.hierarchy[ h ].keys[ 0 ];
-			var nextKey = this.getNextKeyWith( type, h, 1 );
+		} // end switch
 
-			while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
+	}
 
-				prevKey = nextKey;
-				nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
 
-			}
 
-			animationCache.prevKey[ type ] = prevKey;
-			animationCache.nextKey[ type ] = nextKey;
+	// Normalize to remove the closing point by default.
+	var lastPoint = points[ points.length - 1];
+	var EPSILON = 0.0000000001;
+	if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON &&
+			 Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON)
+		points.splice( points.length - 1, 1);
+	if ( closedPath ) {
 
-		}
+		points.push( points[ 0 ] );
 
 	}
 
+	return points;
+
 };
 
-THREE.Animation.prototype.resetBlendWeights = function () {
+//
+// Breaks path into shapes
+//
+//	Assumptions (if parameter isCCW==true the opposite holds):
+//	- solid shapes are defined clockwise (CW)
+//	- holes are defined counterclockwise (CCW)
+//
+//	If parameter noHoles==true:
+//  - all subPaths are regarded as solid shapes
+//  - definition order CW/CCW has no relevance
+//
 
-	for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
+THREE.Path.prototype.toShapes = function( isCCW, noHoles ) {
 
-		var object = this.hierarchy[ h ];
+	function extractSubpaths( inActions ) {
 
-		if ( object.animationCache !== undefined ) {
+		var i, il, item, action, args;
 
-			object.animationCache.blending.positionWeight = 0.0;
-			object.animationCache.blending.quaternionWeight = 0.0;
-			object.animationCache.blending.scaleWeight = 0.0;
+		var subPaths = [], lastPath = new THREE.Path();
 
-		}
+		for ( i = 0, il = inActions.length; i < il; i ++ ) {
 
-	}
-
-};
-
-THREE.Animation.prototype.update = (function(){
-
-	var points = [];
-	var target = new THREE.Vector3();
-	var newVector = new THREE.Vector3();
-	var newQuat = new THREE.Quaternion();
-
-	// Catmull-Rom spline
+			item = inActions[ i ];
 
-	var interpolateCatmullRom = function ( points, scale ) {
+			args = item.args;
+			action = item.action;
 
-		var c = [], v3 = [],
-		point, intPoint, weight, w2, w3,
-		pa, pb, pc, pd;
+			if ( action == THREE.PathActions.MOVE_TO ) {
 
-		point = ( points.length - 1 ) * scale;
-		intPoint = Math.floor( point );
-		weight = point - intPoint;
+				if ( lastPath.actions.length != 0 ) {
 
-		c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
-		c[ 1 ] = intPoint;
-		c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1;
-		c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2;
+					subPaths.push( lastPath );
+					lastPath = new THREE.Path();
 
-		pa = points[ c[ 0 ] ];
-		pb = points[ c[ 1 ] ];
-		pc = points[ c[ 2 ] ];
-		pd = points[ c[ 3 ] ];
+				}
 
-		w2 = weight * weight;
-		w3 = weight * w2;
+			}
 
-		v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 );
-		v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 );
-		v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 );
+			lastPath[ action ].apply( lastPath, args );
 
-		return v3;
+		}
 
-	};
+		if ( lastPath.actions.length != 0 ) {
 
-	var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) {
+			subPaths.push( lastPath );
 
-		var v0 = ( p2 - p0 ) * 0.5,
-			v1 = ( p3 - p1 ) * 0.5;
+		}
 
-		return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
+		// console.log(subPaths);
 
-	};
+		return	subPaths;
+	}
 
-	return function ( delta ) {
+	function toShapesNoHoles( inSubpaths ) {
 
-		if ( this.isPlaying === false ) return;
+		var shapes = [];
 
-		this.currentTime += delta * this.timeScale;
+		for ( var i = 0, il = inSubpaths.length; i < il; i ++ ) {
 
-		if ( this.weight === 0 )
-			return;
+			var tmpPath = inSubpaths[ i ];
 
-		//
+			var tmpShape = new THREE.Shape();
+			tmpShape.actions = tmpPath.actions;
+			tmpShape.curves = tmpPath.curves;
 
-		var duration = this.data.length;
+			shapes.push( tmpShape );
+		}
 
-		if ( this.currentTime > duration || this.currentTime < 0 ) {
+		//console.log("shape", shapes);
 
-			if ( this.loop ) {
+		return shapes;
+	};
 
-				this.currentTime %= duration;
+	function isPointInsidePolygon( inPt, inPolygon ) {
+		var EPSILON = 0.0000000001;
 
-				if ( this.currentTime < 0 )
-					this.currentTime += duration;
+		var polyLen = inPolygon.length;
 
-				this.reset();
+		// inPt on polygon contour => immediate success    or
+		// toggling of inside/outside at every single! intersection point of an edge
+		//  with the horizontal line through inPt, left of inPt
+		//  not counting lowerY endpoints of edges and whole edges on that line
+		var inside = false;
+		for( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {
+			var edgeLowPt  = inPolygon[ p ];
+			var edgeHighPt = inPolygon[ q ];
 
-			} else {
+			var edgeDx = edgeHighPt.x - edgeLowPt.x;
+			var edgeDy = edgeHighPt.y - edgeLowPt.y;
 
-				this.stop();
-				return;
+			if ( Math.abs(edgeDy) > EPSILON ) {			// not parallel
+				if ( edgeDy < 0 ) {
+					edgeLowPt  = inPolygon[ q ]; edgeDx = - edgeDx;
+					edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;
+				}
+				if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) 		continue;
 
+				if ( inPt.y == edgeLowPt.y ) {
+					if ( inPt.x == edgeLowPt.x )		return	true;		// inPt is on contour ?
+					// continue;				// no intersection or edgeLowPt => doesn't count !!!
+				} else {
+					var perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y);
+					if ( perpEdge == 0 )				return	true;		// inPt is on contour ?
+					if ( perpEdge < 0 ) 				continue;
+					inside = ! inside;		// true intersection left of inPt
+				}
+			} else {		// parallel or colinear
+				if ( inPt.y != edgeLowPt.y ) 		continue;			// parallel
+				// egde lies on the same horizontal line as inPt
+				if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||
+					 ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) )		return	true;	// inPt: Point on contour !
+				// continue;
 			}
-
 		}
 
-		for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
-
-			var object = this.hierarchy[ h ];
-			var animationCache = object.animationCache.animations[this.data.name];
-			var blending = object.animationCache.blending;
-
-			// loop through pos/rot/scl
-
-			for ( var t = 0; t < 3; t ++ ) {
-
-				// get keys
-
-				var type    = this.keyTypes[ t ];
-				var prevKey = animationCache.prevKey[ type ];
-				var nextKey = animationCache.nextKey[ type ];
+		return	inside;
+	}
 
-				if ( ( this.timeScale > 0 && nextKey.time <= this.currentTime ) ||
-					( this.timeScale < 0 && prevKey.time >= this.currentTime ) ) {
 
-					prevKey = this.data.hierarchy[ h ].keys[ 0 ];
-					nextKey = this.getNextKeyWith( type, h, 1 );
+	var subPaths = extractSubpaths( this.actions );
+	if ( subPaths.length == 0 ) return [];
 
-					while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
+	if ( noHoles === true )	return	toShapesNoHoles( subPaths );
 
-						prevKey = nextKey;
-						nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
 
-					}
+	var solid, tmpPath, tmpShape, shapes = [];
 
-					animationCache.prevKey[ type ] = prevKey;
-					animationCache.nextKey[ type ] = nextKey;
+	if ( subPaths.length == 1) {
 
-				}
+		tmpPath = subPaths[0];
+		tmpShape = new THREE.Shape();
+		tmpShape.actions = tmpPath.actions;
+		tmpShape.curves = tmpPath.curves;
+		shapes.push( tmpShape );
+		return shapes;
 
-				object.matrixAutoUpdate = true;
-				object.matrixWorldNeedsUpdate = true;
+	}
 
-				var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
+	var holesFirst = ! THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() );
+	holesFirst = isCCW ? ! holesFirst : holesFirst;
 
-				var prevXYZ = prevKey[ type ];
-				var nextXYZ = nextKey[ type ];
+	// console.log("Holes first", holesFirst);
+	
+	var betterShapeHoles = [];
+	var newShapes = [];
+	var newShapeHoles = [];
+	var mainIdx = 0;
+	var tmpPoints;
 
-				if ( scale < 0 ) scale = 0;
-				if ( scale > 1 ) scale = 1;
+	newShapes[mainIdx] = undefined;
+	newShapeHoles[mainIdx] = [];
 
-				// interpolate
+	var i, il;
 
-				if ( type === "pos" ) {
+	for ( i = 0, il = subPaths.length; i < il; i ++ ) {
 
-					if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
+		tmpPath = subPaths[ i ];
+		tmpPoints = tmpPath.getPoints();
+		solid = THREE.Shape.Utils.isClockWise( tmpPoints );
+		solid = isCCW ? ! solid : solid;
 
-						newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
-						newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
-						newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
+		if ( solid ) {
 
-						// blend
-						var proportionalWeight = this.weight / ( this.weight + blending.positionWeight );
-						object.position.lerp( newVector, proportionalWeight );
-						blending.positionWeight += this.weight;
+			if ( (! holesFirst ) && ( newShapes[mainIdx] ) )	mainIdx ++;
 
-					} else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
-								this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+			newShapes[mainIdx] = { s: new THREE.Shape(), p: tmpPoints };
+			newShapes[mainIdx].s.actions = tmpPath.actions;
+			newShapes[mainIdx].s.curves = tmpPath.curves;
+			
+			if ( holesFirst )	mainIdx ++;
+			newShapeHoles[mainIdx] = [];
 
-						points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ];
-						points[ 1 ] = prevXYZ;
-						points[ 2 ] = nextXYZ;
-						points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ];
+			//console.log('cw', i);
 
-						scale = scale * 0.33 + 0.33;
+		} else {
 
-						var currentPoint = interpolateCatmullRom( points, scale );
-						var proportionalWeight = this.weight / ( this.weight + blending.positionWeight );
-						blending.positionWeight += this.weight;
+			newShapeHoles[mainIdx].push( { h: tmpPath, p: tmpPoints[0] } );
 
-						// blend
+			//console.log('ccw', i);
 
-						var vector = object.position;
-						
-						vector.x = vector.x + ( currentPoint[ 0 ] - vector.x ) * proportionalWeight;
-						vector.y = vector.y + ( currentPoint[ 1 ] - vector.y ) * proportionalWeight;
-						vector.z = vector.z + ( currentPoint[ 2 ] - vector.z ) * proportionalWeight;
+		}
 
-						if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+	}
 
-							var forwardPoint = interpolateCatmullRom( points, scale * 1.01 );
+	// only Holes? -> probably all Shapes with wrong orientation
+	if ( ! newShapes[0] )	return	toShapesNoHoles( subPaths );
 
-							target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
-							target.sub( vector );
-							target.y = 0;
-							target.normalize();
 
-							var angle = Math.atan2( target.x, target.z );
-							object.rotation.set( 0, angle, 0 );
+	if ( newShapes.length > 1 ) {
+		var ambigious = false;
+		var toChange = [];
 
+		for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
+			betterShapeHoles[sIdx] = [];
+		}
+		for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
+			var sh = newShapes[sIdx];
+			var sho = newShapeHoles[sIdx];
+			for (var hIdx = 0; hIdx < sho.length; hIdx ++ ) {
+				var ho = sho[hIdx];
+				var hole_unassigned = true;
+				for (var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {
+					if ( isPointInsidePolygon( ho.p, newShapes[s2Idx].p ) ) {
+						if ( sIdx != s2Idx )		toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );
+						if ( hole_unassigned ) {
+							hole_unassigned = false;
+							betterShapeHoles[s2Idx].push( ho );
+						} else {
+							ambigious = true;
 						}
-
 					}
+				}
+				if ( hole_unassigned ) { betterShapeHoles[sIdx].push( ho ); }
+			}
+		}
+		// console.log("ambigious: ", ambigious);
+		if ( toChange.length > 0 ) {
+			// console.log("to change: ", toChange);
+			if (! ambigious)	newShapeHoles = betterShapeHoles;
+		}
+	}
 
-				} else if ( type === "rot" ) {
-
-					THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale );
-
-					// Avoid paying the cost of an additional slerp if we don't have to
-					if ( blending.quaternionWeight === 0 ) {
-
-						object.quaternion.copy(newQuat);
-						blending.quaternionWeight = this.weight;
+	var tmpHoles, j, jl;
+	for ( i = 0, il = newShapes.length; i < il; i ++ ) {
+		tmpShape = newShapes[i].s;
+		shapes.push( tmpShape );
+		tmpHoles = newShapeHoles[i];
+		for ( j = 0, jl = tmpHoles.length; j < jl; j ++ ) {
+			tmpShape.holes.push( tmpHoles[j].h );
+		}
+	}
 
-					} else {
+	//console.log("shape", shapes);
 
-						var proportionalWeight = this.weight / ( this.weight + blending.quaternionWeight );
-						THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight );
-						blending.quaternionWeight += this.weight;
+	return shapes;
 
-					}
+};
 
-				} else if ( type === "scl" ) {
+// File:src/extras/core/Shape.js
 
-					newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
-					newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
-					newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ * Defines a 2d shape plane using paths.
+ **/
 
-					var proportionalWeight = this.weight / ( this.weight + blending.scaleWeight );
-					object.scale.lerp( newVector, proportionalWeight );
-					blending.scaleWeight += this.weight;
+// STEP 1 Create a path.
+// STEP 2 Turn path into shape.
+// STEP 3 ExtrudeGeometry takes in Shape/Shapes
+// STEP 3a - Extract points from each shape, turn to vertices
+// STEP 3b - Triangulate each shape, add faces.
 
-				}
+THREE.Shape = function () {
 
-			}
+	THREE.Path.apply( this, arguments );
+	this.holes = [];
 
-		}
+};
 
-		return true;
+THREE.Shape.prototype = Object.create( THREE.Path.prototype );
 
-	};
+// Convenience method to return ExtrudeGeometry
 
-})();
+THREE.Shape.prototype.extrude = function ( options ) {
 
+	var extruded = new THREE.ExtrudeGeometry( this, options );
+	return extruded;
 
+};
 
+// Convenience method to return ShapeGeometry
 
+THREE.Shape.prototype.makeGeometry = function ( options ) {
 
-// Get next key with
+	var geometry = new THREE.ShapeGeometry( this, options );
+	return geometry;
 
-THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) {
+};
 
-	var keys = this.data.hierarchy[ h ].keys;
+// Get points of holes
 
-	if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
-		 this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+THREE.Shape.prototype.getPointsHoles = function ( divisions ) {
 
-		key = key < keys.length - 1 ? key : keys.length - 1;
+	var i, il = this.holes.length, holesPts = [];
 
-	} else {
+	for ( i = 0; i < il; i ++ ) {
 
-		key = key % keys.length;
+		holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends );
 
 	}
 
-	for ( ; key < keys.length; key ++ ) {
-
-		if ( keys[ key ][ type ] !== undefined ) {
-
-			return keys[ key ];
+	return holesPts;
 
-		}
+};
 
-	}
+// Get points of holes (spaced by regular distance)
 
-	return this.data.hierarchy[ h ].keys[ 0 ];
+THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) {
 
-};
+	var i, il = this.holes.length, holesPts = [];
 
-// Get previous key with
+	for ( i = 0; i < il; i ++ ) {
 
-THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) {
+		holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends );
 
-	var keys = this.data.hierarchy[ h ].keys;
+	}
 
-	if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
-		this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+	return holesPts;
 
-		key = key > 0 ? key : 0;
+};
 
-	} else {
 
-		key = key >= 0 ? key : key + keys.length;
+// Get points of shape and holes (keypoints based on segments parameter)
 
-	}
+THREE.Shape.prototype.extractAllPoints = function ( divisions ) {
 
+	return {
 
-	for ( ; key >= 0; key -- ) {
+		shape: this.getTransformedPoints( divisions ),
+		holes: this.getPointsHoles( divisions )
 
-		if ( keys[ key ][ type ] !== undefined ) {
+	};
 
-			return keys[ key ];
+};
 
-		}
+THREE.Shape.prototype.extractPoints = function ( divisions ) {
 
+	if (this.useSpacedPoints) {
+		return this.extractAllSpacedPoints(divisions);
 	}
 
-	return this.data.hierarchy[ h ].keys[ keys.length - 1 ];
+	return this.extractAllPoints(divisions);
 
 };
 
-// File:src/extras/animation/KeyFrameAnimation.js
-
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- * @author khang duong
- * @author erik kitson
- */
-
-THREE.KeyFrameAnimation = function ( data ) {
+//
+// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) {
+//
+// 	return {
+//
+// 		shape: this.transform( bend, divisions ),
+// 		holes: this.getPointsHoles( divisions, bend )
+//
+// 	};
+//
+// };
 
-	this.root = data.node;
-	this.data = THREE.AnimationHandler.init( data );
-	this.hierarchy = THREE.AnimationHandler.parse( this.root );
-	this.currentTime = 0;
-	this.timeScale = 0.001;
-	this.isPlaying = false;
-	this.isPaused = true;
-	this.loop = true;
+// Get points of shape and holes (spaced by regular distance)
 
-	// initialize to first keyframes
+THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) {
 
-	for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
+	return {
 
-		var keys = this.data.hierarchy[h].keys,
-			sids = this.data.hierarchy[h].sids,
-			obj = this.hierarchy[h];
+		shape: this.getTransformedSpacedPoints( divisions ),
+		holes: this.getSpacedPointsHoles( divisions )
 
-		if ( keys.length && sids ) {
+	};
 
-			for ( var s = 0; s < sids.length; s ++ ) {
+};
 
-				var sid = sids[ s ],
-					next = this.getNextKeyWith( sid, h, 0 );
+/**************************************************************
+ *	Utils
+ **************************************************************/
 
-				if ( next ) {
+THREE.Shape.Utils = {
 
-					next.apply( sid );
+	triangulateShape: function ( contour, holes ) {
 
+		function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) {
+			// inOtherPt needs to be colinear to the inSegment
+			if ( inSegPt1.x != inSegPt2.x ) {
+				if ( inSegPt1.x < inSegPt2.x ) {
+					return	( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) );
+				} else {
+					return	( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) );
+				}
+			} else {
+				if ( inSegPt1.y < inSegPt2.y ) {
+					return	( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) );
+				} else {
+					return	( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) );
 				}
-
 			}
-
-			obj.matrixAutoUpdate = false;
-			this.data.hierarchy[h].node.updateMatrix();
-			obj.matrixWorldNeedsUpdate = true;
-
 		}
 
-	}
-
-};
+		function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) {
+			var EPSILON = 0.0000000001;
 
+			var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x,   seg1dy = inSeg1Pt2.y - inSeg1Pt1.y;
+			var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x,   seg2dy = inSeg2Pt2.y - inSeg2Pt1.y;
 
-THREE.KeyFrameAnimation.prototype.play = function ( startTime ) {
+			var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x;
+			var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y;
 
-	this.currentTime = startTime !== undefined ? startTime : 0;
+			var limit		= seg1dy * seg2dx - seg1dx * seg2dy;
+			var perpSeg1	= seg1dy * seg1seg2dx - seg1dx * seg1seg2dy;
 
-	if ( this.isPlaying === false ) {
+			if ( Math.abs(limit) > EPSILON ) {			// not parallel
 
-		this.isPlaying = true;
+				var perpSeg2;
+				if ( limit > 0 ) {
+					if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) 		return [];
+					perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
+					if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) 		return [];
+				} else {
+					if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) 		return [];
+					perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
+					if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) 		return [];
+				}
 
-		// reset key cache
+				// i.e. to reduce rounding errors
+				// intersection at endpoint of segment#1?
+				if ( perpSeg2 == 0 ) {
+					if ( ( inExcludeAdjacentSegs ) &&
+						 ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) )		return [];
+					return  [ inSeg1Pt1 ];
+				}
+				if ( perpSeg2 == limit ) {
+					if ( ( inExcludeAdjacentSegs ) &&
+						 ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) )		return [];
+					return  [ inSeg1Pt2 ];
+				}
+				// intersection at endpoint of segment#2?
+				if ( perpSeg1 == 0 )		return  [ inSeg2Pt1 ];
+				if ( perpSeg1 == limit )	return  [ inSeg2Pt2 ];
 
-		var h, hl = this.hierarchy.length,
-			object,
-			node;
+				// return real intersection point
+				var factorSeg1 = perpSeg2 / limit;
+				return	[ { x: inSeg1Pt1.x + factorSeg1 * seg1dx,
+							y: inSeg1Pt1.y + factorSeg1 * seg1dy } ];
 
-		for ( h = 0; h < hl; h ++ ) {
+			} else {		// parallel or colinear
+				if ( ( perpSeg1 != 0 ) ||
+					 ( seg2dy * seg1seg2dx != seg2dx * seg1seg2dy ) ) 			return [];
 
-			object = this.hierarchy[ h ];
-			node = this.data.hierarchy[ h ];
-
-			if ( node.animationCache === undefined ) {
-
-				node.animationCache = {};
-				node.animationCache.prevKey = null;
-				node.animationCache.nextKey = null;
-				node.animationCache.originalMatrix = object.matrix;
-
-			}
-
-			var keys = this.data.hierarchy[h].keys;
-
-			if (keys.length) {
-
-				node.animationCache.prevKey = keys[ 0 ];
-				node.animationCache.nextKey = keys[ 1 ];
-
-				this.startTime = Math.min( keys[0].time, this.startTime );
-				this.endTime = Math.max( keys[keys.length - 1].time, this.endTime );
+				// they are collinear or degenerate
+				var seg1Pt = ( (seg1dx == 0) && (seg1dy == 0) );	// segment1 ist just a point?
+				var seg2Pt = ( (seg2dx == 0) && (seg2dy == 0) );	// segment2 ist just a point?
+				// both segments are points
+				if ( seg1Pt && seg2Pt ) {
+					if ( (inSeg1Pt1.x != inSeg2Pt1.x) ||
+						 (inSeg1Pt1.y != inSeg2Pt1.y) )		return [];   	// they are distinct  points
+					return  [ inSeg1Pt1 ];                 					// they are the same point
+				}
+				// segment#1  is a single point
+				if ( seg1Pt ) {
+					if (! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) )		return [];		// but not in segment#2
+					return  [ inSeg1Pt1 ];
+				}
+				// segment#2  is a single point
+				if ( seg2Pt ) {
+					if (! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) )		return [];		// but not in segment#1
+					return  [ inSeg2Pt1 ];
+				}
 
+				// they are collinear segments, which might overlap
+				var seg1min, seg1max, seg1minVal, seg1maxVal;
+				var seg2min, seg2max, seg2minVal, seg2maxVal;
+				if (seg1dx != 0) {		// the segments are NOT on a vertical line
+					if ( inSeg1Pt1.x < inSeg1Pt2.x ) {
+						seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x;
+						seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x;
+					} else {
+						seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x;
+						seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x;
+					}
+					if ( inSeg2Pt1.x < inSeg2Pt2.x ) {
+						seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x;
+						seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x;
+					} else {
+						seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x;
+						seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x;
+					}
+				} else {				// the segments are on a vertical line
+					if ( inSeg1Pt1.y < inSeg1Pt2.y ) {
+						seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y;
+						seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y;
+					} else {
+						seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y;
+						seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y;
+					}
+					if ( inSeg2Pt1.y < inSeg2Pt2.y ) {
+						seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y;
+						seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y;
+					} else {
+						seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y;
+						seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y;
+					}
+				}
+				if ( seg1minVal <= seg2minVal ) {
+					if ( seg1maxVal <  seg2minVal )	return [];
+					if ( seg1maxVal == seg2minVal )	{
+						if ( inExcludeAdjacentSegs )		return [];
+						return [ seg2min ];
+					}
+					if ( seg1maxVal <= seg2maxVal )	return [ seg2min, seg1max ];
+					return	[ seg2min, seg2max ];
+				} else {
+					if ( seg1minVal >  seg2maxVal )	return [];
+					if ( seg1minVal == seg2maxVal )	{
+						if ( inExcludeAdjacentSegs )		return [];
+						return [ seg1min ];
+					}
+					if ( seg1maxVal <= seg2maxVal )	return [ seg1min, seg1max ];
+					return	[ seg1min, seg2max ];
+				}
 			}
-
 		}
 
-		this.update( 0 );
-
-	}
-
-	this.isPaused = false;
-
-	THREE.AnimationHandler.play( this );
-
-};
-
-
-THREE.KeyFrameAnimation.prototype.stop = function() {
-
-	this.isPlaying = false;
-	this.isPaused  = false;
-
-	THREE.AnimationHandler.stop( this );
-
-	// reset JIT matrix and remove cache
+		function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) {
+			// The order of legs is important
 
-	for ( var h = 0; h < this.data.hierarchy.length; h ++ ) {
-		
-		var obj = this.hierarchy[ h ];
-		var node = this.data.hierarchy[ h ];
+			var EPSILON = 0.0000000001;
 
-		if ( node.animationCache !== undefined ) {
+			// translation of all points, so that Vertex is at (0,0)
+			var legFromPtX	= inLegFromPt.x - inVertex.x,  legFromPtY	= inLegFromPt.y - inVertex.y;
+			var legToPtX	= inLegToPt.x	- inVertex.x,  legToPtY		= inLegToPt.y	- inVertex.y;
+			var otherPtX	= inOtherPt.x	- inVertex.x,  otherPtY		= inOtherPt.y	- inVertex.y;
 
-			var original = node.animationCache.originalMatrix;
+			// main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg.
+			var from2toAngle	= legFromPtX * legToPtY - legFromPtY * legToPtX;
+			var from2otherAngle	= legFromPtX * otherPtY - legFromPtY * otherPtX;
 
-			original.copy( obj.matrix );
-			obj.matrix = original;
+			if ( Math.abs(from2toAngle) > EPSILON ) {			// angle != 180 deg.
 
-			delete node.animationCache;
+				var other2toAngle		= otherPtX * legToPtY - otherPtY * legToPtX;
+				// console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle );
 
+				if ( from2toAngle > 0 ) {				// main angle < 180 deg.
+					return	( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) );
+				} else {								// main angle > 180 deg.
+					return	( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) );
+				}
+			} else {										// angle == 180 deg.
+				// console.log( "from2to: 180 deg., from2other: " + from2otherAngle  );
+				return	( from2otherAngle > 0 );
+			}
 		}
 
-	}
-
-};
-
-
-// Update
-
-THREE.KeyFrameAnimation.prototype.update = function ( delta ) {
-
-	if ( this.isPlaying === false ) return;
-
-	this.currentTime += delta * this.timeScale;
-
-	//
-
-	var duration = this.data.length;
-
-	if ( this.loop === true && this.currentTime > duration ) {
-
-		this.currentTime %= duration;
 
-	}
-
-	this.currentTime = Math.min( this.currentTime, duration );
-
-	for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
+		function removeHoles( contour, holes ) {
 
-		var object = this.hierarchy[ h ];
-		var node = this.data.hierarchy[ h ];
+			var shape = contour.concat(); // work on this shape
+			var hole;
 
-		var keys = node.keys,
-			animationCache = node.animationCache;
+			function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) {
+				// Check if hole point lies within angle around shape point
+				var lastShapeIdx = shape.length - 1;
 
+				var prevShapeIdx = inShapeIdx - 1;
+				if ( prevShapeIdx < 0 )			prevShapeIdx = lastShapeIdx;
 
-		if ( keys.length ) {
+				var nextShapeIdx = inShapeIdx + 1;
+				if ( nextShapeIdx > lastShapeIdx )	nextShapeIdx = 0;
 
-			var prevKey = animationCache.prevKey;
-			var nextKey = animationCache.nextKey;
+				var insideAngle = isPointInsideAngle( shape[inShapeIdx], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[inHoleIdx] );
+				if (! insideAngle ) {
+					// console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y );
+					return	false;
+				}
 
-			if ( nextKey.time <= this.currentTime ) {
+				// Check if shape point lies within angle around hole point
+				var lastHoleIdx = hole.length - 1;
 
-				while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
+				var prevHoleIdx = inHoleIdx - 1;
+				if ( prevHoleIdx < 0 )			prevHoleIdx = lastHoleIdx;
 
-					prevKey = nextKey;
-					nextKey = keys[ prevKey.index + 1 ];
+				var nextHoleIdx = inHoleIdx + 1;
+				if ( nextHoleIdx > lastHoleIdx )	nextHoleIdx = 0;
 
+				insideAngle = isPointInsideAngle( hole[inHoleIdx], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[inShapeIdx] );
+				if (! insideAngle ) {
+					// console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y );
+					return	false;
 				}
 
-				animationCache.prevKey = prevKey;
-				animationCache.nextKey = nextKey;
-
+				return	true;
 			}
 
-			if ( nextKey.time >= this.currentTime ) {
-
-				prevKey.interpolate( nextKey, this.currentTime );
-
-			} else {
-
-				prevKey.interpolate( nextKey, nextKey.time );
+			function intersectsShapeEdge( inShapePt, inHolePt ) {
+				// checks for intersections with shape edges
+				var sIdx, nextIdx, intersection;
+				for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) {
+					nextIdx = sIdx+1; nextIdx %= shape.length;
+					intersection = intersect_segments_2D( inShapePt, inHolePt, shape[sIdx], shape[nextIdx], true );
+					if ( intersection.length > 0 )		return	true;
+				}
 
+				return	false;
 			}
 
-			this.data.hierarchy[ h ].node.updateMatrix();
-			object.matrixWorldNeedsUpdate = true;
-
-		}
-
-	}
-
-};
-
-// Get next key with
-
-THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) {
-
-	var keys = this.data.hierarchy[ h ].keys;
-	key = key % keys.length;
-
-	for ( ; key < keys.length; key ++ ) {
-
-		if ( keys[ key ].hasTarget( sid ) ) {
-
-			return keys[ key ];
-
-		}
-
-	}
-
-	return keys[ 0 ];
-
-};
-
-// Get previous key with
-
-THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) {
-
-	var keys = this.data.hierarchy[ h ].keys;
-	key = key >= 0 ? key : key + keys.length;
-
-	for ( ; key >= 0; key -- ) {
-
-		if ( keys[ key ].hasTarget( sid ) ) {
-
-			return keys[ key ];
-
-		}
-
-	}
-
-	return keys[ keys.length - 1 ];
-
-};
-
-// File:src/extras/animation/MorphAnimation.js
-
-/**
- * @author mrdoob / http://mrdoob.com
- */
-
-THREE.MorphAnimation = function ( mesh ) {
-
-	this.mesh = mesh;
-	this.frames = mesh.morphTargetInfluences.length;
-	this.currentTime = 0;
-	this.duration = 1000;
-	this.loop = true;
-
-	this.isPlaying = false;
-
-};
-
-THREE.MorphAnimation.prototype = {
-
-	play: function () {
-
-		this.isPlaying = true;
+			var indepHoles = [];
 
-	},
+			function intersectsHoleEdge( inShapePt, inHolePt ) {
+				// checks for intersections with hole edges
+				var ihIdx, chkHole,
+					hIdx, nextIdx, intersection;
+				for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) {
+					chkHole = holes[indepHoles[ihIdx]];
+					for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) {
+						nextIdx = hIdx+1; nextIdx %= chkHole.length;
+						intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[hIdx], chkHole[nextIdx], true );
+						if ( intersection.length > 0 )		return	true;
+					}
+				}
+				return	false;
+			}
 
-	pause: function () {
+			var holeIndex, shapeIndex,
+				shapePt, holePt,
+				holeIdx, cutKey, failedCuts = [],
+				tmpShape1, tmpShape2,
+				tmpHole1, tmpHole2;
 
-		this.isPlaying = false;
+			for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
 
-	},
+				indepHoles.push( h );
 
-	update: ( function () {
+			}
 
-		var lastFrame = 0;
-		var currentFrame = 0;
+			var minShapeIndex = 0;
+			var counter = indepHoles.length * 2;
+			while ( indepHoles.length > 0 ) {
+				counter --;
+				if ( counter < 0 ) {
+					console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" );
+					break;
+				}
 
-		return function ( delta ) {
+				// search for shape-vertex and hole-vertex,
+				// which can be connected without intersections
+				for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) {
 
-			if ( this.isPlaying === false ) return;
+					shapePt = shape[ shapeIndex ];
+					holeIndex	= - 1;
 
-			this.currentTime += delta;
+					// search for hole which can be reached without intersections
+					for ( var h = 0; h < indepHoles.length; h ++ ) {
+						holeIdx = indepHoles[h];
 
-			if ( this.loop === true && this.currentTime > this.duration ) {
+						// prevent multiple checks
+						cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx;
+						if ( failedCuts[cutKey] !== undefined )			continue;
 
-				this.currentTime %= this.duration;
+						hole = holes[holeIdx];
+						for ( var h2 = 0; h2 < hole.length; h2 ++ ) {
+							holePt = hole[ h2 ];
+							if (! isCutLineInsideAngles( shapeIndex, h2 ) )		continue;
+							if ( intersectsShapeEdge( shapePt, holePt ) )		continue;
+							if ( intersectsHoleEdge( shapePt, holePt ) )		continue;
 
-			}
+							holeIndex = h2;
+							indepHoles.splice(h,1);
 
-			this.currentTime = Math.min( this.currentTime, this.duration );
+							tmpShape1 = shape.slice( 0, shapeIndex+1 );
+							tmpShape2 = shape.slice( shapeIndex );
+							tmpHole1 = hole.slice( holeIndex );
+							tmpHole2 = hole.slice( 0, holeIndex+1 );
 
-			var interpolation = this.duration / this.frames;
-			var frame = Math.floor( this.currentTime / interpolation );
+							shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
 
-			if ( frame != currentFrame ) {
+							minShapeIndex = shapeIndex;
 
-				this.mesh.morphTargetInfluences[ lastFrame ] = 0;
-				this.mesh.morphTargetInfluences[ currentFrame ] = 1;
-				this.mesh.morphTargetInfluences[ frame ] = 0;
+							// Debug only, to show the selected cuts
+							// glob_CutLines.push( [ shapePt, holePt ] );
 
-				lastFrame = currentFrame;
-				currentFrame = frame;
+							break;
+						}
+						if ( holeIndex >= 0 )	break;		// hole-vertex found
 
+						failedCuts[cutKey] = true;			// remember failure
+					}
+					if ( holeIndex >= 0 )	break;		// hole-vertex found
+				}
 			}
 
-			this.mesh.morphTargetInfluences[ frame ] = ( this.currentTime % interpolation ) / interpolation;
-			this.mesh.morphTargetInfluences[ lastFrame ] = 1 - this.mesh.morphTargetInfluences[ frame ];
-
+			return shape; 			/* shape with no holes */
 		}
 
-	} )()
 
-};
+		var i, il, f, face,
+			key, index,
+			allPointsMap = {};
 
-// File:src/extras/geometries/BoxGeometry.js
+		// To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first.
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as
- */
+		var allpoints = contour.concat();
 
-THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
+		for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
 
-	THREE.Geometry.call( this );
+			Array.prototype.push.apply( allpoints, holes[h] );
 
-	this.type = 'BoxGeometry';
+		}
 
-	this.parameters = {
-		width: width,
-		height: height,
-		depth: depth,
-		widthSegments: widthSegments,
-		heightSegments: heightSegments,
-		depthSegments: depthSegments
-	};
+		//console.log( "allpoints",allpoints, allpoints.length );
 
-	this.widthSegments = widthSegments || 1;
-	this.heightSegments = heightSegments || 1;
-	this.depthSegments = depthSegments || 1;
+		// prepare all points map
 
-	var scope = this;
+		for ( i = 0, il = allpoints.length; i < il; i ++ ) {
 
-	var width_half = width / 2;
-	var height_half = height / 2;
-	var depth_half = depth / 2;
+			key = allpoints[ i ].x + ":" + allpoints[ i ].y;
 
-	buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, 0 ); // px
-	buildPlane( 'z', 'y',   1, - 1, depth, height, - width_half, 1 ); // nx
-	buildPlane( 'x', 'z',   1,   1, width, depth, height_half, 2 ); // py
-	buildPlane( 'x', 'z',   1, - 1, width, depth, - height_half, 3 ); // ny
-	buildPlane( 'x', 'y',   1, - 1, width, height, depth_half, 4 ); // pz
-	buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, 5 ); // nz
+			if ( allPointsMap[ key ] !== undefined ) {
 
-	function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) {
+				console.log( "Duplicate point", key );
 
-		var w, ix, iy,
-		gridX = scope.widthSegments,
-		gridY = scope.heightSegments,
-		width_half = width / 2,
-		height_half = height / 2,
-		offset = scope.vertices.length;
+			}
 
-		if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) {
+			allPointsMap[ key ] = i;
 
-			w = 'z';
+		}
 
-		} else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) {
+		// remove holes by cutting paths to holes and adding them to the shape
+		var shapeWithoutHoles = removeHoles( contour, holes );
 
-			w = 'y';
-			gridY = scope.depthSegments;
+		var triangles = THREE.FontUtils.Triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape
+		//console.log( "triangles",triangles, triangles.length );
 
-		} else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {
+		// check all face vertices against all points map
 
-			w = 'x';
-			gridX = scope.depthSegments;
+		for ( i = 0, il = triangles.length; i < il; i ++ ) {
 
-		}
+			face = triangles[ i ];
 
-		var gridX1 = gridX + 1,
-		gridY1 = gridY + 1,
-		segment_width = width / gridX,
-		segment_height = height / gridY,
-		normal = new THREE.Vector3();
+			for ( f = 0; f < 3; f ++ ) {
 
-		normal[ w ] = depth > 0 ? 1 : - 1;
+				key = face[ f ].x + ":" + face[ f ].y;
 
-		for ( iy = 0; iy < gridY1; iy ++ ) {
+				index = allPointsMap[ key ];
 
-			for ( ix = 0; ix < gridX1; ix ++ ) {
+				if ( index !== undefined ) {
 
-				var vector = new THREE.Vector3();
-				vector[ u ] = ( ix * segment_width - width_half ) * udir;
-				vector[ v ] = ( iy * segment_height - height_half ) * vdir;
-				vector[ w ] = depth;
+					face[ f ] = index;
 
-				scope.vertices.push( vector );
+				}
 
 			}
 
 		}
 
-		for ( iy = 0; iy < gridY; iy ++ ) {
+		return triangles.concat();
 
-			for ( ix = 0; ix < gridX; ix ++ ) {
+	},
 
-				var a = ix + gridX1 * iy;
-				var b = ix + gridX1 * ( iy + 1 );
-				var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
-				var d = ( ix + 1 ) + gridX1 * iy;
+	isClockWise: function ( pts ) {
 
-				var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY );
-				var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY );
-				var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY );
-				var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY );
+		return THREE.FontUtils.Triangulate.area( pts ) < 0;
 
-				var face = new THREE.Face3( a + offset, b + offset, d + offset );
-				face.normal.copy( normal );
-				face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
-				face.materialIndex = materialIndex;
+	},
 
-				scope.faces.push( face );
-				scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
+	// Bezier Curves formulas obtained from
+	// http://en.wikipedia.org/wiki/B%C3%A9zier_curve
 
-				face = new THREE.Face3( b + offset, c + offset, d + offset );
-				face.normal.copy( normal );
-				face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
-				face.materialIndex = materialIndex;
+	// Quad Bezier Functions
 
-				scope.faces.push( face );
-				scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
+	b2p0: function ( t, p ) {
 
-			}
+		var k = 1 - t;
+		return k * k * p;
 
-		}
+	},
 
-	}
+	b2p1: function ( t, p ) {
 
-	this.mergeVertices();
+		return 2 * ( 1 - t ) * t * p;
 
-};
+	},
 
-THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype );
+	b2p2: function ( t, p ) {
 
-// File:src/extras/geometries/CircleGeometry.js
+		return t * t * p;
 
-/**
- * @author hughes
- */
+	},
 
-THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) {
+	b2: function ( t, p0, p1, p2 ) {
 
-	THREE.Geometry.call( this );
+		return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 );
 
-	this.type = 'CircleGeometry';
+	},
 
-	this.parameters = {
-		radius: radius,
-		segments: segments,
-		thetaStart: thetaStart,
-		thetaLength: thetaLength
-	};
+	// Cubic Bezier Functions
 
-	radius = radius || 50;
-	segments = segments !== undefined ? Math.max( 3, segments ) : 8;
+	b3p0: function ( t, p ) {
 
-	thetaStart = thetaStart !== undefined ? thetaStart : 0;
-	thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
+		var k = 1 - t;
+		return k * k * k * p;
 
-	var i, uvs = [],
-	center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 );
+	},
 
-	this.vertices.push(center);
-	uvs.push( centerUV );
+	b3p1: function ( t, p ) {
 
-	for ( i = 0; i <= segments; i ++ ) {
+		var k = 1 - t;
+		return 3 * k * k * t * p;
 
-		var vertex = new THREE.Vector3();
-		var segment = thetaStart + i / segments * thetaLength;
+	},
 
-		vertex.x = radius * Math.cos( segment );
-		vertex.y = radius * Math.sin( segment );
+	b3p2: function ( t, p ) {
 
-		this.vertices.push( vertex );
-		uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, ( vertex.y / radius + 1 ) / 2 ) );
+		var k = 1 - t;
+		return 3 * k * t * t * p;
 
-	}
+	},
 
-	var n = new THREE.Vector3( 0, 0, 1 );
+	b3p3: function ( t, p ) {
 
-	for ( i = 1; i <= segments; i ++ ) {
+		return t * t * t * p;
 
-		this.faces.push( new THREE.Face3( i, i + 1, 0, [ n.clone(), n.clone(), n.clone() ] ) );
-		this.faceVertexUvs[ 0 ].push( [ uvs[ i ].clone(), uvs[ i + 1 ].clone(), centerUV.clone() ] );
+	},
 
-	}
+	b3: function ( t, p0, p1, p2, p3 ) {
 
-	this.computeFaceNormals();
+		return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) +  this.b3p3( t, p3 );
 
-	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+	}
 
 };
 
-THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype );
-
-// File:src/extras/geometries/CubeGeometry.js
-
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
 
-THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
-
-	console.warn( 'THREE.CubeGeometry has been renamed to THREE.BoxGeometry.' );
-	return new THREE.BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments );
-
- };
-
-// File:src/extras/geometries/CylinderGeometry.js
+// File:src/extras/curves/LineCurve.js
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+/**************************************************************
+ *	Line
+ **************************************************************/
 
-THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded ) {
+THREE.LineCurve = function ( v1, v2 ) {
 
-	THREE.Geometry.call( this );
+	this.v1 = v1;
+	this.v2 = v2;
 
-	this.type = 'CylinderGeometry';
+};
 
-	this.parameters = {
-		radiusTop: radiusTop,
-		radiusBottom: radiusBottom,
-		height: height,
-		radialSegments: radialSegments,
-		heightSegments: heightSegments,
-		openEnded: openEnded
-	};
+THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype );
 
-	radiusTop = radiusTop !== undefined ? radiusTop : 20;
-	radiusBottom = radiusBottom !== undefined ? radiusBottom : 20;
-	height = height !== undefined ? height : 100;
+THREE.LineCurve.prototype.getPoint = function ( t ) {
 
-	radialSegments = radialSegments || 8;
-	heightSegments = heightSegments || 1;
+	var point = this.v2.clone().sub(this.v1);
+	point.multiplyScalar( t ).add( this.v1 );
 
-	openEnded = openEnded !== undefined ? openEnded : false;
+	return point;
 
-	var heightHalf = height / 2;
+};
 
-	var x, y, vertices = [], uvs = [];
+// Line curve is linear, so we can overwrite default getPointAt
 
-	for ( y = 0; y <= heightSegments; y ++ ) {
+THREE.LineCurve.prototype.getPointAt = function ( u ) {
 
-		var verticesRow = [];
-		var uvsRow = [];
+	return this.getPoint( u );
 
-		var v = y / heightSegments;
-		var radius = v * ( radiusBottom - radiusTop ) + radiusTop;
+};
 
-		for ( x = 0; x <= radialSegments; x ++ ) {
+THREE.LineCurve.prototype.getTangent = function( t ) {
 
-			var u = x / radialSegments;
+	var tangent = this.v2.clone().sub(this.v1);
 
-			var vertex = new THREE.Vector3();
-			vertex.x = radius * Math.sin( u * Math.PI * 2 );
-			vertex.y = - v * height + heightHalf;
-			vertex.z = radius * Math.cos( u * Math.PI * 2 );
+	return tangent.normalize();
 
-			this.vertices.push( vertex );
+};
 
-			verticesRow.push( this.vertices.length - 1 );
-			uvsRow.push( new THREE.Vector2( u, 1 - v ) );
+// File:src/extras/curves/QuadraticBezierCurve.js
 
-		}
+/**************************************************************
+ *	Quadratic Bezier curve
+ **************************************************************/
 
-		vertices.push( verticesRow );
-		uvs.push( uvsRow );
 
-	}
+THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) {
 
-	var tanTheta = ( radiusBottom - radiusTop ) / height;
-	var na, nb;
+	this.v0 = v0;
+	this.v1 = v1;
+	this.v2 = v2;
 
-	for ( x = 0; x < radialSegments; x ++ ) {
+};
 
-		if ( radiusTop !== 0 ) {
+THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype );
 
-			na = this.vertices[ vertices[ 0 ][ x ] ].clone();
-			nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone();
 
-		} else {
+THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) {
 
-			na = this.vertices[ vertices[ 1 ][ x ] ].clone();
-			nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone();
+	var vector = new THREE.Vector2();
 
-		}
+	vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
+	vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
 
-		na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize();
-		nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize();
+	return vector;
 
-		for ( y = 0; y < heightSegments; y ++ ) {
+};
 
-			var v1 = vertices[ y ][ x ];
-			var v2 = vertices[ y + 1 ][ x ];
-			var v3 = vertices[ y + 1 ][ x + 1 ];
-			var v4 = vertices[ y ][ x + 1 ];
 
-			var n1 = na.clone();
-			var n2 = na.clone();
-			var n3 = nb.clone();
-			var n4 = nb.clone();
+THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {
 
-			var uv1 = uvs[ y ][ x ].clone();
-			var uv2 = uvs[ y + 1 ][ x ].clone();
-			var uv3 = uvs[ y + 1 ][ x + 1 ].clone();
-			var uv4 = uvs[ y ][ x + 1 ].clone();
+	var vector = new THREE.Vector2();
 
-			this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
-			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
+	vector.x = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x );
+	vector.y = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y );
 
-			this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) );
-			this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
+	// returns unit vector
 
-		}
+	return vector.normalize();
 
-	}
+};
 
-	// top cap
+// File:src/extras/curves/CubicBezierCurve.js
 
-	if ( openEnded === false && radiusTop > 0 ) {
+/**************************************************************
+ *	Cubic Bezier curve
+ **************************************************************/
 
-		this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) );
+THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) {
 
-		for ( x = 0; x < radialSegments; x ++ ) {
+	this.v0 = v0;
+	this.v1 = v1;
+	this.v2 = v2;
+	this.v3 = v3;
 
-			var v1 = vertices[ 0 ][ x ];
-			var v2 = vertices[ 0 ][ x + 1 ];
-			var v3 = this.vertices.length - 1;
+};
 
-			var n1 = new THREE.Vector3( 0, 1, 0 );
-			var n2 = new THREE.Vector3( 0, 1, 0 );
-			var n3 = new THREE.Vector3( 0, 1, 0 );
+THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype );
 
-			var uv1 = uvs[ 0 ][ x ].clone();
-			var uv2 = uvs[ 0 ][ x + 1 ].clone();
-			var uv3 = new THREE.Vector2( uv2.x, 0 );
+THREE.CubicBezierCurve.prototype.getPoint = function ( t ) {
 
-			this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
-			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
+	var tx, ty;
 
-		}
+	tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
+	ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
 
-	}
+	return new THREE.Vector2( tx, ty );
 
-	// bottom cap
+};
 
-	if ( openEnded === false && radiusBottom > 0 ) {
+THREE.CubicBezierCurve.prototype.getTangent = function( t ) {
 
-		this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) );
+	var tx, ty;
 
-		for ( x = 0; x < radialSegments; x ++ ) {
+	tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
+	ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
 
-			var v1 = vertices[ y ][ x + 1 ];
-			var v2 = vertices[ y ][ x ];
-			var v3 = this.vertices.length - 1;
+	var tangent = new THREE.Vector2( tx, ty );
+	tangent.normalize();
 
-			var n1 = new THREE.Vector3( 0, - 1, 0 );
-			var n2 = new THREE.Vector3( 0, - 1, 0 );
-			var n3 = new THREE.Vector3( 0, - 1, 0 );
+	return tangent;
 
-			var uv1 = uvs[ y ][ x + 1 ].clone();
-			var uv2 = uvs[ y ][ x ].clone();
-			var uv3 = new THREE.Vector2( uv2.x, 1 );
+};
 
-			this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
-			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
+// File:src/extras/curves/SplineCurve.js
 
-		}
+/**************************************************************
+ *	Spline curve
+ **************************************************************/
 
-	}
+THREE.SplineCurve = function ( points /* array of Vector2 */ ) {
 
-	this.computeFaceNormals();
+	this.points = ( points == undefined ) ? [] : points;
 
-}
+};
 
-THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype );
 
-// File:src/extras/geometries/ExtrudeGeometry.js
+THREE.SplineCurve.prototype.getPoint = function ( t ) {
 
-/**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- *
- * Creates extruded geometry from a path shape.
- *
- * parameters = {
- *
- *  curveSegments: <int>, // number of points on the curves
- *  steps: <int>, // number of points for z-side extrusions / used for subdividing segements of extrude spline too
- *  amount: <int>, // Depth to extrude the shape
- *
- *  bevelEnabled: <bool>, // turn on bevel
- *  bevelThickness: <float>, // how deep into the original shape bevel goes
- *  bevelSize: <float>, // how far from shape outline is bevel
- *  bevelSegments: <int>, // number of bevel layers
- *
- *  extrudePath: <THREE.CurvePath> // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined)
- *  frames: <THREE.TubeGeometry.FrenetFrames> // containing arrays of tangents, normals, binormals
- *
- *  material: <int> // material index for front and back faces
- *  extrudeMaterial: <int> // material index for extrusion and beveled faces
- *  uvGenerator: <Object> // object that provides UV generator functions
- *
- * }
- **/
+	var points = this.points;
+	var point = ( points.length - 1 ) * t;
 
-THREE.ExtrudeGeometry = function ( shapes, options ) {
+	var intPoint = Math.floor( point );
+	var weight = point - intPoint;
 
-	if ( typeof( shapes ) === "undefined" ) {
-		shapes = [];
-		return;
-	}
+	var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ]
+	var point1 = points[ intPoint ]
+	var point2 = points[ intPoint > points.length - 2 ? points.length -1 : intPoint + 1 ]
+	var point3 = points[ intPoint > points.length - 3 ? points.length -1 : intPoint + 2 ]
 
-	THREE.Geometry.call( this );
+	var vector = new THREE.Vector2();
 
-	this.type = 'ExtrudeGeometry';
+	vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight );
+	vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight );
 
-	shapes = shapes instanceof Array ? shapes : [ shapes ];
+	return vector;
 
-	this.addShapeList( shapes, options );
+};
 
-	this.computeFaceNormals();
+// File:src/extras/curves/EllipseCurve.js
 
-	// can't really use automatic vertex normals
-	// as then front and back sides get smoothed too
-	// should do separate smoothing just for sides
+/**************************************************************
+ *	Ellipse curve
+ **************************************************************/
 
-	//this.computeVertexNormals();
+THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) {
 
-	//console.log( "took", ( Date.now() - startTime ) );
+	this.aX = aX;
+	this.aY = aY;
 
-};
+	this.xRadius = xRadius;
+	this.yRadius = yRadius;
 
-THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+	this.aStartAngle = aStartAngle;
+	this.aEndAngle = aEndAngle;
 
-THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) {
-	var sl = shapes.length;
+	this.aClockwise = aClockwise;
 
-	for ( var s = 0; s < sl; s ++ ) {
-		var shape = shapes[ s ];
-		this.addShape( shape, options );
-	}
 };
 
-THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) {
+THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype );
 
-	var amount = options.amount !== undefined ? options.amount : 100;
+THREE.EllipseCurve.prototype.getPoint = function ( t ) {
 
-	var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10
-	var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8
-	var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
+	var deltaAngle = this.aEndAngle - this.aStartAngle;
 
-	var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
+	if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2;
+	if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2;
 
-	var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
+	var angle;
 
-	var steps = options.steps !== undefined ? options.steps : 1;
+	if ( this.aClockwise === true ) {
 
-	var extrudePath = options.extrudePath;
-	var extrudePts, extrudeByPath = false;
+		angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle );
 
-	var material = options.material;
-	var extrudeMaterial = options.extrudeMaterial;
+	} else {
 
-	// Use default WorldUVGenerator if no UV generators are specified.
-	var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator;
+		angle = this.aStartAngle + t * deltaAngle;
 
-	var splineTube, binormal, normal, position2;
-	if ( extrudePath ) {
+	}
+	
+	var vector = new THREE.Vector2();
 
-		extrudePts = extrudePath.getSpacedPoints( steps );
+	vector.x = this.aX + this.xRadius * Math.cos( angle );
+	vector.y = this.aY + this.yRadius * Math.sin( angle );
 
-		extrudeByPath = true;
-		bevelEnabled = false; // bevels not supported for path extrusion
+	return vector;
 
-		// SETUP TNB variables
+};
 
-		// Reuse TNB from TubeGeomtry for now.
-		// TODO1 - have a .isClosed in spline?
+// File:src/extras/curves/ArcCurve.js
 
-		splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false);
+/**************************************************************
+ *	Arc curve
+ **************************************************************/
 
-		// console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
+THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
 
-		binormal = new THREE.Vector3();
-		normal = new THREE.Vector3();
-		position2 = new THREE.Vector3();
+	THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
+};
 
-	}
+THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype );
 
-	// Safeguards if bevels are not enabled
+// File:src/extras/curves/LineCurve3.js
 
-	if ( ! bevelEnabled ) {
+/**************************************************************
+ *	Line3D
+ **************************************************************/
 
-		bevelSegments = 0;
-		bevelThickness = 0;
-		bevelSize = 0;
+THREE.LineCurve3 = THREE.Curve.create(
 
-	}
+	function ( v1, v2 ) {
 
-	// Variables initalization
+		this.v1 = v1;
+		this.v2 = v2;
 
-	var ahole, h, hl; // looping of holes
-	var scope = this;
-	var bevelPoints = [];
+	},
 
-	var shapesOffset = this.vertices.length;
+	function ( t ) {
 
-	var shapePoints = shape.extractPoints( curveSegments );
+		var vector = new THREE.Vector3();
 
-	var vertices = shapePoints.shape;
-	var holes = shapePoints.holes;
+		vector.subVectors( this.v2, this.v1 ); // diff
+		vector.multiplyScalar( t );
+		vector.add( this.v1 );
 
-	var reverse = ! THREE.Shape.Utils.isClockWise( vertices ) ;
+		return vector;
 
-	if ( reverse ) {
+	}
 
-		vertices = vertices.reverse();
+);
 
-		// Maybe we should also check if holes are in the opposite direction, just to be safe ...
+// File:src/extras/curves/QuadraticBezierCurve3.js
 
-		for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+/**************************************************************
+ *	Quadratic Bezier 3D curve
+ **************************************************************/
 
-			ahole = holes[ h ];
+THREE.QuadraticBezierCurve3 = THREE.Curve.create(
 
-			if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
+	function ( v0, v1, v2 ) {
 
-				holes[ h ] = ahole.reverse();
+		this.v0 = v0;
+		this.v1 = v1;
+		this.v2 = v2;
 
-			}
+	},
 
-		}
+	function ( t ) {
 
-		reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
+		var vector = new THREE.Vector3();
 
-	}
+		vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
+		vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
+		vector.z = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z );
 
+		return vector;
 
-	var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
+	}
 
-	/* Vertices */
+);
 
-	var contour = vertices; // vertices has all points but contour has only points of circumference
+// File:src/extras/curves/CubicBezierCurve3.js
 
-	for ( h = 0, hl = holes.length;  h < hl; h ++ ) {
+/**************************************************************
+ *	Cubic Bezier 3D curve
+ **************************************************************/
 
-		ahole = holes[ h ];
+THREE.CubicBezierCurve3 = THREE.Curve.create(
 
-		vertices = vertices.concat( ahole );
+	function ( v0, v1, v2, v3 ) {
 
-	}
+		this.v0 = v0;
+		this.v1 = v1;
+		this.v2 = v2;
+		this.v3 = v3;
 
+	},
 
-	function scalePt2 ( pt, vec, size ) {
+	function ( t ) {
 
-		if ( ! vec ) console.log( "die" );
+		var vector = new THREE.Vector3();
 
-		return vec.clone().multiplyScalar( size ).add( pt );
+		vector.x = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
+		vector.y = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
+		vector.z = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z );
+
+		return vector;
 
 	}
 
-	var b, bs, t, z,
-		vert, vlen = vertices.length,
-		face, flen = faces.length,
-		cont, clen = contour.length;
+);
 
+// File:src/extras/curves/SplineCurve3.js
 
-	// Find directions for point movement
+/**************************************************************
+ *	Spline 3D curve
+ **************************************************************/
 
-	var RAD_TO_DEGREES = 180 / Math.PI;
 
+THREE.SplineCurve3 = THREE.Curve.create(
 
-	function getBevelVec( inPt, inPrev, inNext ) {
+	function ( points /* array of Vector3 */) {
 
-		var EPSILON = 0.0000000001;
-		
-		// computes for inPt the corresponding point inPt' on a new contour
-		//   shiftet by 1 unit (length of normalized vector) to the left
-		// if we walk along contour clockwise, this new contour is outside the old one
-		//
-		// inPt' is the intersection of the two lines parallel to the two
-		//  adjacent edges of inPt at a distance of 1 unit on the left side.
-		
-		var v_trans_x, v_trans_y, shrink_by = 1;		// resulting translation vector for inPt
+		this.points = ( points == undefined ) ? [] : points;
 
-		// good reading for geometry algorithms (here: line-line intersection)
-		// http://geomalgorithms.com/a05-_intersect-1.html
+	},
 
-		var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y;
-		var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y;
-		
-		var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );
-		
-		// check for colinear edges
-		var colinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );
-		
-		if ( Math.abs( colinear0 ) > EPSILON ) {		// not colinear
-			
-			// length of vectors for normalizing
-	
-			var v_prev_len = Math.sqrt( v_prev_lensq );
-			var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );
-			
-			// shift adjacent points by unit vectors to the left
-	
-			var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );
-			var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );
-			
-			var ptNextShift_x = ( inNext.x - v_next_y / v_next_len );
-			var ptNextShift_y = ( inNext.y + v_next_x / v_next_len );
-	
-			// scaling factor for v_prev to intersection point
-	
-			var sf = (  ( ptNextShift_x - ptPrevShift_x ) * v_next_y -
-						( ptNextShift_y - ptPrevShift_y ) * v_next_x    ) /
-					  ( v_prev_x * v_next_y - v_prev_y * v_next_x );
-	
-			// vector from inPt to intersection point
-	
-			v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );
-			v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );
-	
-			// Don't normalize!, otherwise sharp corners become ugly
-			//  but prevent crazy spikes
-			var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y )
-			if ( v_trans_lensq <= 2 ) {
-				return	new THREE.Vector2( v_trans_x, v_trans_y );
-			} else {
-				shrink_by = Math.sqrt( v_trans_lensq / 2 );
-			}
-			
-		} else {		// handle special case of colinear edges
+	function ( t ) {
 
-			var direction_eq = false;		// assumes: opposite
-			if ( v_prev_x > EPSILON ) {
-				if ( v_next_x > EPSILON ) { direction_eq = true; }
-			} else {
-				if ( v_prev_x < - EPSILON ) {
-					if ( v_next_x < - EPSILON ) { direction_eq = true; }
-				} else {
-					if ( Math.sign(v_prev_y) == Math.sign(v_next_y) ) { direction_eq = true; }
-				}
-			}
+		var points = this.points;
+		var point = ( points.length - 1 ) * t;
 
-			if ( direction_eq ) {
-				// console.log("Warning: lines are a straight sequence");
-				v_trans_x = - v_prev_y;
-				v_trans_y =  v_prev_x;
-				shrink_by = Math.sqrt( v_prev_lensq );
-			} else {
-				// console.log("Warning: lines are a straight spike");
-				v_trans_x = v_prev_x;
-				v_trans_y = v_prev_y;
-				shrink_by = Math.sqrt( v_prev_lensq / 2 );
-			}
+		var intPoint = Math.floor( point );
+		var weight = point - intPoint;
 
-		}
+		var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ];
+		var point1 = points[ intPoint ];
+		var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
+		var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];
+
+		var vector = new THREE.Vector3();
+
+		vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight );
+		vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight );
+		vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight );
 
-		return	new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );
+		return vector;
 
 	}
 
+);
 
-	var contourMovements = [];
-
-	for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
+// File:src/extras/curves/ClosedSplineCurve3.js
 
-		if ( j === il ) j = 0;
-		if ( k === il ) k = 0;
+/**************************************************************
+ *	Closed Spline 3D curve
+ **************************************************************/
 
-		//  (j)---(i)---(k)
-		// console.log('i,j,k', i, j , k)
 
-		var pt_i = contour[ i ];
-		var pt_j = contour[ j ];
-		var pt_k = contour[ k ];
+THREE.ClosedSplineCurve3 = THREE.Curve.create(
 
-		contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
+	function ( points /* array of Vector3 */) {
 
-	}
+		this.points = ( points == undefined ) ? [] : points;
 
-	var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
+	},
 
-	for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+	function ( t ) {
 
-		ahole = holes[ h ];
+		var points = this.points;
+		var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1
 
-		oneHoleMovements = [];
+		var intPoint = Math.floor( point );
+		var weight = point - intPoint;
 
-		for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
+		intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;
 
-			if ( j === il ) j = 0;
-			if ( k === il ) k = 0;
+		var point0 = points[ ( intPoint - 1 ) % points.length ];
+		var point1 = points[ ( intPoint     ) % points.length ];
+		var point2 = points[ ( intPoint + 1 ) % points.length ];
+		var point3 = points[ ( intPoint + 2 ) % points.length ];
 
-			//  (j)---(i)---(k)
-			oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
+		var vector = new THREE.Vector3();
 
-		}
+		vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight );
+		vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight );
+		vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight );
 
-		holesMovements.push( oneHoleMovements );
-		verticesMovements = verticesMovements.concat( oneHoleMovements );
+		return vector;
 
 	}
 
+);
 
-	// Loop bevelSegments, 1 for the front, 1 for the back
-
-	for ( b = 0; b < bevelSegments; b ++ ) {
-	//for ( b = bevelSegments; b > 0; b -- ) {
-
-		t = b / bevelSegments;
-		z = bevelThickness * ( 1 - t );
-
-		//z = bevelThickness * t;
-		bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved
-		//bs = bevelSize * t ; // linear
-
-		// contract shape
+// File:src/extras/animation/AnimationHandler.js
 
-		for ( i = 0, il = contour.length; i < il; i ++ ) {
+/**
+ * @author mikael emtinger / http://gomo.se/
+ */
 
-			vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
+THREE.AnimationHandler = {
 
-			v( vert.x, vert.y,  - z );
+	LINEAR: 0,
+	CATMULLROM: 1,
+	CATMULLROM_FORWARD: 2,
 
-		}
+	//
 
-		// expand holes
+	add: function () { console.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); },
+	get: function () { console.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); },
+	remove: function () { console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); },
 
-		for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+	//
 
-			ahole = holes[ h ];
-			oneHoleMovements = holesMovements[ h ];
+	animations: [],
 
-			for ( i = 0, il = ahole.length; i < il; i ++ ) {
+	init: function ( data ) {
 
-				vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
+		if ( data.initialized === true ) return;
 
-				v( vert.x, vert.y,  - z );
+		// loop through all keys
 
-			}
+		for ( var h = 0; h < data.hierarchy.length; h ++ ) {
 
-		}
+			for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
 
-	}
+				// remove minus times
 
-	bs = bevelSize;
+				if ( data.hierarchy[ h ].keys[ k ].time < 0 ) {
 
-	// Back facing vertices
+					 data.hierarchy[ h ].keys[ k ].time = 0;
 
-	for ( i = 0; i < vlen; i ++ ) {
+				}
 
-		vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
+				// create quaternions
 
-		if ( ! extrudeByPath ) {
+				if ( data.hierarchy[ h ].keys[ k ].rot !== undefined &&
+				  ! ( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) {
 
-			v( vert.x, vert.y, 0 );
+					var quat = data.hierarchy[ h ].keys[ k ].rot;
+					data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion().fromArray( quat );
 
-		} else {
+				}
 
-			// v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
+			}
 
-			normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x);
-			binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y);
+			// prepare morph target keys
 
-			position2.copy( extrudePts[0] ).add(normal).add(binormal);
+			if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) {
 
-			v( position2.x, position2.y, position2.z );
+				// get all used
 
-		}
+				var usedMorphTargets = {};
 
-	}
+				for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
 
-	// Add stepped vertices...
-	// Including front facing vertices
+					for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
 
-	var s;
+						var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ];
+						usedMorphTargets[ morphTargetName ] = - 1;
 
-	for ( s = 1; s <= steps; s ++ ) {
+					}
 
-		for ( i = 0; i < vlen; i ++ ) {
+				}
 
-			vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
+				data.hierarchy[ h ].usedMorphTargets = usedMorphTargets;
 
-			if ( ! extrudeByPath ) {
 
-				v( vert.x, vert.y, amount / steps * s );
+				// set all used on all frames
 
-			} else {
+				for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
 
-				// v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
+					var influences = {};
 
-				normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x );
-				binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y );
+					for ( var morphTargetName in usedMorphTargets ) {
 
-				position2.copy( extrudePts[s] ).add( normal ).add( binormal );
+						for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
 
-				v( position2.x, position2.y, position2.z );
+							if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) {
 
-			}
+								influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ];
+								break;
 
-		}
+							}
 
-	}
+						}
 
+						if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) {
 
-	// Add bevel segments planes
+							influences[ morphTargetName ] = 0;
 
-	//for ( b = 1; b <= bevelSegments; b ++ ) {
-	for ( b = bevelSegments - 1; b >= 0; b -- ) {
+						}
 
-		t = b / bevelSegments;
-		z = bevelThickness * ( 1 - t );
-		//bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) );
-		bs = bevelSize * Math.sin ( t * Math.PI/2 ) ;
+					}
 
-		// contract shape
+					data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences;
 
-		for ( i = 0, il = contour.length; i < il; i ++ ) {
+				}
 
-			vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
-			v( vert.x, vert.y,  amount + z );
+			}
 
-		}
 
-		// expand holes
+			// remove all keys that are on the same time
 
-		for ( h = 0, hl = holes.length; h < hl; h ++ ) {
+			for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) {
 
-			ahole = holes[ h ];
-			oneHoleMovements = holesMovements[ h ];
+				if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) {
 
-			for ( i = 0, il = ahole.length; i < il; i ++ ) {
+					data.hierarchy[ h ].keys.splice( k, 1 );
+					k --;
 
-				vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
+				}
 
-				if ( ! extrudeByPath ) {
+			}
 
-					v( vert.x, vert.y,  amount + z );
 
-				} else {
+			// set index
 
-					v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
+			for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
 
-				}
+				data.hierarchy[ h ].keys[ k ].index = k;
 
 			}
 
 		}
 
-	}
-
-	/* Faces */
+		data.initialized = true;
 
-	// Top and bottom faces
+		return data;
 
-	buildLidFaces();
+	},
 
-	// Sides faces
+	parse: function ( root ) {
 
-	buildSideFaces();
+		var parseRecurseHierarchy = function ( root, hierarchy ) {
 
+			hierarchy.push( root );
 
-	/////  Internal functions
+			for ( var c = 0; c < root.children.length; c ++ )
+				parseRecurseHierarchy( root.children[ c ], hierarchy );
 
-	function buildLidFaces() {
+		};
 
-		if ( bevelEnabled ) {
+		// setup hierarchy
 
-			var layer = 0 ; // steps + 1
-			var offset = vlen * layer;
+		var hierarchy = [];
 
-			// Bottom faces
+		if ( root instanceof THREE.SkinnedMesh ) {
 
-			for ( i = 0; i < flen; i ++ ) {
+			for ( var b = 0; b < root.skeleton.bones.length; b ++ ) {
 
-				face = faces[ i ];
-				f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset );
+				hierarchy.push( root.skeleton.bones[ b ] );
 
 			}
 
-			layer = steps + bevelSegments * 2;
-			offset = vlen * layer;
+		} else {
 
-			// Top faces
+			parseRecurseHierarchy( root, hierarchy );
 
-			for ( i = 0; i < flen; i ++ ) {
+		}
 
-				face = faces[ i ];
-				f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );
+		return hierarchy;
 
-			}
+	},
 
-		} else {
+	play: function ( animation ) {
 
-			// Bottom faces
+		if ( this.animations.indexOf( animation ) === - 1 ) {
 
-			for ( i = 0; i < flen; i ++ ) {
+			this.animations.push( animation );
 
-				face = faces[ i ];
-				f3( face[ 2 ], face[ 1 ], face[ 0 ] );
+		}
 
-			}
+	},
 
-			// Top faces
+	stop: function ( animation ) {
+
+		var index = this.animations.indexOf( animation );
 
-			for ( i = 0; i < flen; i ++ ) {
+		if ( index !== - 1 ) {
 
-				face = faces[ i ];
-				f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );
+			this.animations.splice( index, 1 );
 
-			}
 		}
 
-	}
+	},
 
-	// Create faces for the z-sides of the shape
+	update: function ( deltaTimeMS ) {
 
-	function buildSideFaces() {
+		for ( var i = 0; i < this.animations.length; i ++ ) {
 
-		var layeroffset = 0;
-		sidewalls( contour, layeroffset );
-		layeroffset += contour.length;
+			this.animations[ i ].resetBlendWeights( );
 
-		for ( h = 0, hl = holes.length;  h < hl; h ++ ) {
+		}
 
-			ahole = holes[ h ];
-			sidewalls( ahole, layeroffset );
+		for ( var i = 0; i < this.animations.length; i ++ ) {
 
-			//, true
-			layeroffset += ahole.length;
+			this.animations[ i ].update( deltaTimeMS );
 
 		}
 
 	}
 
-	function sidewalls( contour, layeroffset ) {
-
-		var j, k;
-		i = contour.length;
-
-		while ( --i >= 0 ) {
+};
 
-			j = i;
-			k = i - 1;
-			if ( k < 0 ) k = contour.length - 1;
+// File:src/extras/animation/Animation.js
 
-			//console.log('b', i,j, i-1, k,vertices.length);
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-			var s = 0, sl = steps  + bevelSegments * 2;
+THREE.Animation = function ( root, data ) {
 
-			for ( s = 0; s < sl; s ++ ) {
+	this.root = root;
+	this.data = THREE.AnimationHandler.init( data );
+	this.hierarchy = THREE.AnimationHandler.parse( root );
 
-				var slen1 = vlen * s;
-				var slen2 = vlen * ( s + 1 );
+	this.currentTime = 0;
+	this.timeScale = 1;
 
-				var a = layeroffset + j + slen1,
-					b = layeroffset + k + slen1,
-					c = layeroffset + k + slen2,
-					d = layeroffset + j + slen2;
+	this.isPlaying = false;
+	this.loop = true;
+	this.weight = 0;
 
-				f4( a, b, c, d, contour, s, sl, j, k );
+	this.interpolationType = THREE.AnimationHandler.LINEAR;
 
-			}
-		}
+};
 
-	}
 
+THREE.Animation.prototype.keyTypes = [ "pos", "rot", "scl" ];
 
-	function v( x, y, z ) {
 
-		scope.vertices.push( new THREE.Vector3( x, y, z ) );
+THREE.Animation.prototype.play = function ( startTime, weight ) {
 
-	}
+	this.currentTime = startTime !== undefined ? startTime : 0;
+	this.weight = weight !== undefined ? weight: 1;
 
-	function f3( a, b, c ) {
+	this.isPlaying = true;
 
-		a += shapesOffset;
-		b += shapesOffset;
-		c += shapesOffset;
+	this.reset();
 
-		// normal, color, material
-		scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
+	THREE.AnimationHandler.play( this );
 
-		var uvs = uvgen.generateTopUV( scope, a, b, c );
+};
 
- 		scope.faceVertexUvs[ 0 ].push( uvs );
 
-	}
+THREE.Animation.prototype.stop = function() {
 
-	function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) {
+	this.isPlaying = false;
 
-		a += shapesOffset;
-		b += shapesOffset;
-		c += shapesOffset;
-		d += shapesOffset;
+	THREE.AnimationHandler.stop( this );
 
- 		scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) );
- 		scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) );
+};
 
- 		var uvs = uvgen.generateSideWallUV( scope, a, b, c, d );
+THREE.Animation.prototype.reset = function () {
 
- 		scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] );
- 		scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] );
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
 
-	}
+		var object = this.hierarchy[ h ];
 
-};
+		object.matrixAutoUpdate = true;
 
-THREE.ExtrudeGeometry.WorldUVGenerator = {
+		if ( object.animationCache === undefined ) {
 
-	generateTopUV: function ( geometry, indexA, indexB, indexC ) {
+			object.animationCache = {
+				animations: {},
+				blending: {
+					positionWeight: 0.0,
+					quaternionWeight: 0.0,
+					scaleWeight: 0.0
+				}
+			};
+		}
 
-		var vertices = geometry.vertices;
+		if ( object.animationCache.animations[this.data.name] === undefined ) {
 
-		var a = vertices[ indexA ];
-		var b = vertices[ indexB ];
-		var c = vertices[ indexC ];
+			object.animationCache.animations[this.data.name] = {};
+			object.animationCache.animations[this.data.name].prevKey = { pos: 0, rot: 0, scl: 0 };
+			object.animationCache.animations[this.data.name].nextKey = { pos: 0, rot: 0, scl: 0 };
+			object.animationCache.animations[this.data.name].originalMatrix = object.matrix;
 
-		return [
-			new THREE.Vector2( a.x, a.y ),
-			new THREE.Vector2( b.x, b.y ),
-			new THREE.Vector2( c.x, c.y )
-		];
+		}
 
-	},
+		var animationCache = object.animationCache.animations[this.data.name];
 
-	generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) {
+		// Get keys to match our current time
 
-		var vertices = geometry.vertices;
+		for ( var t = 0; t < 3; t ++ ) {
 
-		var a = vertices[ indexA ];
-		var b = vertices[ indexB ];
-		var c = vertices[ indexC ];
-		var d = vertices[ indexD ];
+			var type = this.keyTypes[ t ];
 
-		if ( Math.abs( a.y - b.y ) < 0.01 ) {
-			return [
-				new THREE.Vector2( a.x, 1 - a.z ),
-				new THREE.Vector2( b.x, 1 - b.z ),
-				new THREE.Vector2( c.x, 1 - c.z ),
-				new THREE.Vector2( d.x, 1 - d.z )
-			];
-		} else {
-			return [
-				new THREE.Vector2( a.y, 1 - a.z ),
-				new THREE.Vector2( b.y, 1 - b.z ),
-				new THREE.Vector2( c.y, 1 - c.z ),
-				new THREE.Vector2( d.y, 1 - d.z )
-			];
-		}
-	}
-};
+			var prevKey = this.data.hierarchy[ h ].keys[ 0 ];
+			var nextKey = this.getNextKeyWith( type, h, 1 );
 
-// File:src/extras/geometries/ShapeGeometry.js
+			while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
 
-/**
- * @author jonobr1 / http://jonobr1.com
- *
- * Creates a one-sided polygonal geometry from a path shape. Similar to
- * ExtrudeGeometry.
- *
- * parameters = {
- *
- *	curveSegments: <int>, // number of points on the curves. NOT USED AT THE MOMENT.
- *
- *	material: <int> // material index for front and back faces
- *	uvGenerator: <Object> // object that provides UV generator functions
- *
- * }
- **/
+				prevKey = nextKey;
+				nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
 
-THREE.ShapeGeometry = function ( shapes, options ) {
+			}
 
-	THREE.Geometry.call( this );
+			animationCache.prevKey[ type ] = prevKey;
+			animationCache.nextKey[ type ] = nextKey;
 
-	this.type = 'ShapeGeometry';
+		}
 
-	if ( shapes instanceof Array === false ) shapes = [ shapes ];
+	}
 
-	this.addShapeList( shapes, options );
+};
 
-	this.computeFaceNormals();
+THREE.Animation.prototype.resetBlendWeights = function () {
 
-};
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
 
-THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+		var object = this.hierarchy[ h ];
 
-/**
- * Add an array of shapes to THREE.ShapeGeometry.
- */
-THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) {
+		if ( object.animationCache !== undefined ) {
 
-	for ( var i = 0, l = shapes.length; i < l; i ++ ) {
+			object.animationCache.blending.positionWeight = 0.0;
+			object.animationCache.blending.quaternionWeight = 0.0;
+			object.animationCache.blending.scaleWeight = 0.0;
 
-		this.addShape( shapes[ i ], options );
+		}
 
 	}
 
-	return this;
-
 };
 
-/**
- * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry.
- */
-THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) {
-
-	if ( options === undefined ) options = {};
-	var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
-
-	var material = options.material;
-	var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator;
+THREE.Animation.prototype.update = (function(){
 
-	//
+	var points = [];
+	var target = new THREE.Vector3();
+	var newVector = new THREE.Vector3();
+	var newQuat = new THREE.Quaternion();
 
-	var i, l, hole, s;
+	// Catmull-Rom spline
 
-	var shapesOffset = this.vertices.length;
-	var shapePoints = shape.extractPoints( curveSegments );
+	var interpolateCatmullRom = function ( points, scale ) {
 
-	var vertices = shapePoints.shape;
-	var holes = shapePoints.holes;
+		var c = [], v3 = [],
+		point, intPoint, weight, w2, w3,
+		pa, pb, pc, pd;
 
-	var reverse = ! THREE.Shape.Utils.isClockWise( vertices );
+		point = ( points.length - 1 ) * scale;
+		intPoint = Math.floor( point );
+		weight = point - intPoint;
 
-	if ( reverse ) {
+		c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
+		c[ 1 ] = intPoint;
+		c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1;
+		c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2;
 
-		vertices = vertices.reverse();
+		pa = points[ c[ 0 ] ];
+		pb = points[ c[ 1 ] ];
+		pc = points[ c[ 2 ] ];
+		pd = points[ c[ 3 ] ];
 
-		// Maybe we should also check if holes are in the opposite direction, just to be safe...
+		w2 = weight * weight;
+		w3 = weight * w2;
 
-		for ( i = 0, l = holes.length; i < l; i ++ ) {
+		v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 );
+		v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 );
+		v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 );
 
-			hole = holes[ i ];
+		return v3;
 
-			if ( THREE.Shape.Utils.isClockWise( hole ) ) {
+	};
 
-				holes[ i ] = hole.reverse();
+	var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) {
 
-			}
+		var v0 = ( p2 - p0 ) * 0.5,
+			v1 = ( p3 - p1 ) * 0.5;
 
-		}
+		return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
 
-		reverse = false;
+	};
 
-	}
+	return function ( delta ) {
 
-	var faces = THREE.Shape.Utils.triangulateShape( vertices, holes );
+		if ( this.isPlaying === false ) return;
 
-	// Vertices
+		this.currentTime += delta * this.timeScale;
 
-	var contour = vertices;
+		if ( this.weight === 0 )
+			return;
 
-	for ( i = 0, l = holes.length; i < l; i ++ ) {
+		//
 
-		hole = holes[ i ];
-		vertices = vertices.concat( hole );
+		var duration = this.data.length;
 
-	}
+		if ( this.currentTime > duration || this.currentTime < 0 ) {
 
-	//
+			if ( this.loop ) {
 
-	var vert, vlen = vertices.length;
-	var face, flen = faces.length;
-	var cont, clen = contour.length;
+				this.currentTime %= duration;
 
-	for ( i = 0; i < vlen; i ++ ) {
+				if ( this.currentTime < 0 )
+					this.currentTime += duration;
 
-		vert = vertices[ i ];
+				this.reset();
 
-		this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) );
+			} else {
 
-	}
+				this.stop();
+				return;
 
-	for ( i = 0; i < flen; i ++ ) {
+			}
 
-		face = faces[ i ];
+		}
 
-		var a = face[ 0 ] + shapesOffset;
-		var b = face[ 1 ] + shapesOffset;
-		var c = face[ 2 ] + shapesOffset;
+		for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
 
-		this.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
-		this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) );
+			var object = this.hierarchy[ h ];
+			var animationCache = object.animationCache.animations[this.data.name];
+			var blending = object.animationCache.blending;
 
-	}
+			// loop through pos/rot/scl
 
-};
+			for ( var t = 0; t < 3; t ++ ) {
 
-// File:src/extras/geometries/LatheGeometry.js
+				// get keys
 
-/**
- * @author astrodud / http://astrodud.isgreat.org/
- * @author zz85 / https://github.com/zz85
- * @author bhouston / http://exocortex.com
- */
+				var type    = this.keyTypes[ t ];
+				var prevKey = animationCache.prevKey[ type ];
+				var nextKey = animationCache.nextKey[ type ];
 
-// points - to create a closed torus, one must use a set of points 
-//    like so: [ a, b, c, d, a ], see first is the same as last.
-// segments - the number of circumference segments to create
-// phiStart - the starting radian
-// phiLength - the radian (0 to 2*PI) range of the lathed section
-//    2*pi is a closed lathe, less than 2PI is a portion.
+				if ( ( this.timeScale > 0 && nextKey.time <= this.currentTime ) ||
+					( this.timeScale < 0 && prevKey.time >= this.currentTime ) ) {
 
-THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) {
+					prevKey = this.data.hierarchy[ h ].keys[ 0 ];
+					nextKey = this.getNextKeyWith( type, h, 1 );
 
-	THREE.Geometry.call( this );
+					while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
 
-	this.type = 'LatheGeometry';
+						prevKey = nextKey;
+						nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
 
-	this.parameters = {
-		points: points,
-		segments: segments,
-		phiStart: phiStart,
-		phiLength: phiLength
-	};
+					}
 
-	segments = segments || 12;
-	phiStart = phiStart || 0;
-	phiLength = phiLength || 2 * Math.PI;
+					animationCache.prevKey[ type ] = prevKey;
+					animationCache.nextKey[ type ] = nextKey;
 
-	var inversePointLength = 1.0 / ( points.length - 1 );
-	var inverseSegments = 1.0 / segments;
+				}
 
-	for ( var i = 0, il = segments; i <= il; i ++ ) {
+				object.matrixAutoUpdate = true;
+				object.matrixWorldNeedsUpdate = true;
 
-		var phi = phiStart + i * inverseSegments * phiLength;
+				var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
 
-		var c = Math.cos( phi ),
-			s = Math.sin( phi );
+				var prevXYZ = prevKey[ type ];
+				var nextXYZ = nextKey[ type ];
 
-		for ( var j = 0, jl = points.length; j < jl; j ++ ) {
+				if ( scale < 0 ) scale = 0;
+				if ( scale > 1 ) scale = 1;
 
-			var pt = points[ j ];
+				// interpolate
 
-			var vertex = new THREE.Vector3();
+				if ( type === "pos" ) {
 
-			vertex.x = c * pt.x - s * pt.y;
-			vertex.y = s * pt.x + c * pt.y;
-			vertex.z = pt.z;
+					if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
 
-			this.vertices.push( vertex );
+						newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
+						newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
+						newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
 
-		}
+						// blend
+						var proportionalWeight = this.weight / ( this.weight + blending.positionWeight );
+						object.position.lerp( newVector, proportionalWeight );
+						blending.positionWeight += this.weight;
 
-	}
+					} else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
+								this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
 
-	var np = points.length;
+						points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ];
+						points[ 1 ] = prevXYZ;
+						points[ 2 ] = nextXYZ;
+						points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ];
 
-	for ( var i = 0, il = segments; i < il; i ++ ) {
+						scale = scale * 0.33 + 0.33;
 
-		for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) {
+						var currentPoint = interpolateCatmullRom( points, scale );
+						var proportionalWeight = this.weight / ( this.weight + blending.positionWeight );
+						blending.positionWeight += this.weight;
 
-			var base = j + np * i;
-			var a = base;
-			var b = base + np;
-			var c = base + 1 + np;
-			var d = base + 1;
+						// blend
 
-			var u0 = i * inverseSegments;
-			var v0 = j * inversePointLength;
-			var u1 = u0 + inverseSegments;
-			var v1 = v0 + inversePointLength;
+						var vector = object.position;
+						
+						vector.x = vector.x + ( currentPoint[ 0 ] - vector.x ) * proportionalWeight;
+						vector.y = vector.y + ( currentPoint[ 1 ] - vector.y ) * proportionalWeight;
+						vector.z = vector.z + ( currentPoint[ 2 ] - vector.z ) * proportionalWeight;
 
-			this.faces.push( new THREE.Face3( a, b, d ) );
+						if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
 
-			this.faceVertexUvs[ 0 ].push( [
+							var forwardPoint = interpolateCatmullRom( points, scale * 1.01 );
 
-				new THREE.Vector2( u0, v0 ),
-				new THREE.Vector2( u1, v0 ),
-				new THREE.Vector2( u0, v1 )
+							target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
+							target.sub( vector );
+							target.y = 0;
+							target.normalize();
 
-			] );
+							var angle = Math.atan2( target.x, target.z );
+							object.rotation.set( 0, angle, 0 );
 
-			this.faces.push( new THREE.Face3( b, c, d ) );
+						}
 
-			this.faceVertexUvs[ 0 ].push( [
+					}
 
-				new THREE.Vector2( u1, v0 ),
-				new THREE.Vector2( u1, v1 ),
-				new THREE.Vector2( u0, v1 )
+				} else if ( type === "rot" ) {
 
-			] );
+					THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale );
 
+					// Avoid paying the cost of an additional slerp if we don't have to
+					if ( blending.quaternionWeight === 0 ) {
 
-		}
+						object.quaternion.copy(newQuat);
+						blending.quaternionWeight = this.weight;
 
-	}
+					} else {
 
-	this.mergeVertices();
-	this.computeFaceNormals();
-	this.computeVertexNormals();
+						var proportionalWeight = this.weight / ( this.weight + blending.quaternionWeight );
+						THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight );
+						blending.quaternionWeight += this.weight;
 
-};
+					}
 
-THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype );
+				} else if ( type === "scl" ) {
 
-// File:src/extras/geometries/PlaneGeometry.js
+					newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
+					newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
+					newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
- */
+					var proportionalWeight = this.weight / ( this.weight + blending.scaleWeight );
+					object.scale.lerp( newVector, proportionalWeight );
+					blending.scaleWeight += this.weight;
 
-THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) {
+				}
 
-	console.info( 'THREE.PlaneGeometry: Consider using THREE.PlaneBufferGeometry for lower memory footprint.' );
+			}
 
-	THREE.Geometry.call( this );
+		}
 
-	this.type = 'PlaneGeometry';
+		return true;
 
-	this.parameters = {
-		width: width,
-		height: height,
-		widthSegments: widthSegments,
-		heightSegments: heightSegments
 	};
 
-	this.fromBufferGeometry( new THREE.PlaneBufferGeometry( width, height, widthSegments, heightSegments ) );
-
-};
+})();
 
-THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-// File:src/extras/geometries/PlaneBufferGeometry.js
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
- */
 
-THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) {
 
-	THREE.BufferGeometry.call( this );
+// Get next key with
 
-	this.type = 'PlaneBufferGeometry';
+THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) {
 
-	this.parameters = {
-		width: width,
-		height: height,
-		widthSegments: widthSegments,
-		heightSegments: heightSegments
-	};
+	var keys = this.data.hierarchy[ h ].keys;
 
-	var width_half = width / 2;
-	var height_half = height / 2;
+	if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
+		 this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
 
-	var gridX = widthSegments || 1;
-	var gridY = heightSegments || 1;
+		key = key < keys.length - 1 ? key : keys.length - 1;
 
-	var gridX1 = gridX + 1;
-	var gridY1 = gridY + 1;
+	} else {
 
-	var segment_width = width / gridX;
-	var segment_height = height / gridY;
+		key = key % keys.length;
 
-	var vertices = new Float32Array( gridX1 * gridY1 * 3 );
-	var normals = new Float32Array( gridX1 * gridY1 * 3 );
-	var uvs = new Float32Array( gridX1 * gridY1 * 2 );
+	}
 
-	var offset = 0;
-	var offset2 = 0;
+	for ( ; key < keys.length; key ++ ) {
 
-	for ( var iy = 0; iy < gridY1; iy ++ ) {
+		if ( keys[ key ][ type ] !== undefined ) {
 
-		var y = iy * segment_height - height_half;
+			return keys[ key ];
 
-		for ( var ix = 0; ix < gridX1; ix ++ ) {
+		}
 
-			var x = ix * segment_width - width_half;
+	}
 
-			vertices[ offset     ] = x;
-			vertices[ offset + 1 ] = - y;
+	return this.data.hierarchy[ h ].keys[ 0 ];
 
-			normals[ offset + 2 ] = 1;
+};
 
-			uvs[ offset2     ] = ix / gridX;
-			uvs[ offset2 + 1 ] = 1 - ( iy / gridY );
+// Get previous key with
 
-			offset += 3;
-			offset2 += 2;
+THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) {
 
-		}
+	var keys = this.data.hierarchy[ h ].keys;
 
-	}
+	if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
+		this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
 
-	offset = 0;
+		key = key > 0 ? key : 0;
 
-	var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 );
+	} else {
 
-	for ( var iy = 0; iy < gridY; iy ++ ) {
+		key = key >= 0 ? key : key + keys.length;
 
-		for ( var ix = 0; ix < gridX; ix ++ ) {
+	}
 
-			var a = ix + gridX1 * iy;
-			var b = ix + gridX1 * ( iy + 1 );
-			var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
-			var d = ( ix + 1 ) + gridX1 * iy;
 
-			indices[ offset     ] = a;
-			indices[ offset + 1 ] = b;
-			indices[ offset + 2 ] = d;
+	for ( ; key >= 0; key -- ) {
 
-			indices[ offset + 3 ] = b;
-			indices[ offset + 4 ] = c;
-			indices[ offset + 5 ] = d;
+		if ( keys[ key ][ type ] !== undefined ) {
 
-			offset += 6;
+			return keys[ key ];
 
 		}
 
 	}
 
-	this.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) );
-	this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
-	this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
-	this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
+	return this.data.hierarchy[ h ].keys[ keys.length - 1 ];
 
 };
 
-THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
-
-// File:src/extras/geometries/RingGeometry.js
+// File:src/extras/animation/KeyFrameAnimation.js
 
 /**
- * @author Kaleb Murphy
+ * @author mikael emtinger / http://gomo.se/
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author khang duong
+ * @author erik kitson
  */
 
-THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {
+THREE.KeyFrameAnimation = function ( data ) {
 
-	THREE.Geometry.call( this );
+	this.root = data.node;
+	this.data = THREE.AnimationHandler.init( data );
+	this.hierarchy = THREE.AnimationHandler.parse( this.root );
+	this.currentTime = 0;
+	this.timeScale = 0.001;
+	this.isPlaying = false;
+	this.isPaused = true;
+	this.loop = true;
 
-	this.type = 'RingGeometry';
+	// initialize to first keyframes
 
-	this.parameters = {
-		innerRadius: innerRadius,
-		outerRadius: outerRadius,
-		thetaSegments: thetaSegments,
-		phiSegments: phiSegments,
-		thetaStart: thetaStart,
-		thetaLength: thetaLength
-	};
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
 
-	innerRadius = innerRadius || 0;
-	outerRadius = outerRadius || 50;
+		var keys = this.data.hierarchy[h].keys,
+			sids = this.data.hierarchy[h].sids,
+			obj = this.hierarchy[h];
 
-	thetaStart = thetaStart !== undefined ? thetaStart : 0;
-	thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
+		if ( keys.length && sids ) {
 
-	thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;
-	phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8;
+			for ( var s = 0; s < sids.length; s ++ ) {
 
-	var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );
+				var sid = sids[ s ],
+					next = this.getNextKeyWith( sid, h, 0 );
 
-	for ( i = 0; i < phiSegments + 1; i ++ ) { // concentric circles inside ring
+				if ( next ) {
 
-		for ( o = 0; o < thetaSegments + 1; o ++ ) { // number of segments per circle
+					next.apply( sid );
 
-			var vertex = new THREE.Vector3();
-			var segment = thetaStart + o / thetaSegments * thetaLength;
-			vertex.x = radius * Math.cos( segment );
-			vertex.y = radius * Math.sin( segment );
+				}
 
-			this.vertices.push( vertex );
-			uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) );
-		}
+			}
 
-		radius += radiusStep;
+			obj.matrixAutoUpdate = false;
+			this.data.hierarchy[h].node.updateMatrix();
+			obj.matrixWorldNeedsUpdate = true;
+
+		}
 
 	}
 
-	var n = new THREE.Vector3( 0, 0, 1 );
+};
 
-	for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring
 
-		var thetaSegment = i * (thetaSegments + 1);
+THREE.KeyFrameAnimation.prototype.play = function ( startTime ) {
 
-		for ( o = 0; o < thetaSegments ; o ++ ) { // number of segments per circle
+	this.currentTime = startTime !== undefined ? startTime : 0;
 
-			var segment = o + thetaSegment;
+	if ( this.isPlaying === false ) {
 
-			var v1 = segment;
-			var v2 = segment + thetaSegments + 1;
-			var v3 = segment + thetaSegments + 2;
+		this.isPlaying = true;
 
-			this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
-			this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]);
+		// reset key cache
 
-			v1 = segment;
-			v2 = segment + thetaSegments + 2;
-			v3 = segment + 1;
+		var h, hl = this.hierarchy.length,
+			object,
+			node;
 
-			this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
-			this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]);
+		for ( h = 0; h < hl; h ++ ) {
+
+			object = this.hierarchy[ h ];
+			node = this.data.hierarchy[ h ];
+
+			if ( node.animationCache === undefined ) {
+
+				node.animationCache = {};
+				node.animationCache.prevKey = null;
+				node.animationCache.nextKey = null;
+				node.animationCache.originalMatrix = object.matrix;
+
+			}
+
+			var keys = this.data.hierarchy[h].keys;
+
+			if (keys.length) {
+
+				node.animationCache.prevKey = keys[ 0 ];
+				node.animationCache.nextKey = keys[ 1 ];
+
+				this.startTime = Math.min( keys[0].time, this.startTime );
+				this.endTime = Math.max( keys[keys.length - 1].time, this.endTime );
+
+			}
 
 		}
+
+		this.update( 0 );
+
 	}
 
-	this.computeFaceNormals();
+	this.isPaused = false;
 
-	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+	THREE.AnimationHandler.play( this );
 
 };
 
-THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
+THREE.KeyFrameAnimation.prototype.stop = function() {
 
-// File:src/extras/geometries/SphereGeometry.js
+	this.isPlaying = false;
+	this.isPaused  = false;
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+	THREE.AnimationHandler.stop( this );
 
-THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
+	// reset JIT matrix and remove cache
 
-	THREE.Geometry.call( this );
+	for ( var h = 0; h < this.data.hierarchy.length; h ++ ) {
+		
+		var obj = this.hierarchy[ h ];
+		var node = this.data.hierarchy[ h ];
 
-	this.type = 'SphereGeometry';
+		if ( node.animationCache !== undefined ) {
 
-	this.parameters = {
-		radius: radius,
-		widthSegments: widthSegments,
-		heightSegments: heightSegments,
-		phiStart: phiStart,
-		phiLength: phiLength,
-		thetaStart: thetaStart,
-		thetaLength: thetaLength 
-	};
+			var original = node.animationCache.originalMatrix;
 
-	radius = radius || 50;
+			original.copy( obj.matrix );
+			obj.matrix = original;
 
-	widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );
-	heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 );
+			delete node.animationCache;
 
-	phiStart = phiStart !== undefined ? phiStart : 0;
-	phiLength = phiLength !== undefined ? phiLength : Math.PI * 2;
+		}
 
-	thetaStart = thetaStart !== undefined ? thetaStart : 0;
-	thetaLength = thetaLength !== undefined ? thetaLength : Math.PI;
+	}
 
-	var x, y, vertices = [], uvs = [];
+};
 
-	for ( y = 0; y <= heightSegments; y ++ ) {
 
-		var verticesRow = [];
-		var uvsRow = [];
+// Update
 
-		for ( x = 0; x <= widthSegments; x ++ ) {
+THREE.KeyFrameAnimation.prototype.update = function ( delta ) {
 
-			var u = x / widthSegments;
-			var v = y / heightSegments;
+	if ( this.isPlaying === false ) return;
 
-			var vertex = new THREE.Vector3();
-			vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
-			vertex.y = radius * Math.cos( thetaStart + v * thetaLength );
-			vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
+	this.currentTime += delta * this.timeScale;
 
-			this.vertices.push( vertex );
+	//
+
+	var duration = this.data.length;
+
+	if ( this.loop === true && this.currentTime > duration ) {
+
+		this.currentTime %= duration;
+
+	}
+
+	this.currentTime = Math.min( this.currentTime, duration );
+
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
 
-			verticesRow.push( this.vertices.length - 1 );
-			uvsRow.push( new THREE.Vector2( u, 1 - v ) );
+		var object = this.hierarchy[ h ];
+		var node = this.data.hierarchy[ h ];
 
-		}
+		var keys = node.keys,
+			animationCache = node.animationCache;
 
-		vertices.push( verticesRow );
-		uvs.push( uvsRow );
 
-	}
+		if ( keys.length ) {
 
-	for ( y = 0; y < heightSegments; y ++ ) {
+			var prevKey = animationCache.prevKey;
+			var nextKey = animationCache.nextKey;
 
-		for ( x = 0; x < widthSegments; x ++ ) {
+			if ( nextKey.time <= this.currentTime ) {
 
-			var v1 = vertices[ y ][ x + 1 ];
-			var v2 = vertices[ y ][ x ];
-			var v3 = vertices[ y + 1 ][ x ];
-			var v4 = vertices[ y + 1 ][ x + 1 ];
+				while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
 
-			var n1 = this.vertices[ v1 ].clone().normalize();
-			var n2 = this.vertices[ v2 ].clone().normalize();
-			var n3 = this.vertices[ v3 ].clone().normalize();
-			var n4 = this.vertices[ v4 ].clone().normalize();
+					prevKey = nextKey;
+					nextKey = keys[ prevKey.index + 1 ];
 
-			var uv1 = uvs[ y ][ x + 1 ].clone();
-			var uv2 = uvs[ y ][ x ].clone();
-			var uv3 = uvs[ y + 1 ][ x ].clone();
-			var uv4 = uvs[ y + 1 ][ x + 1 ].clone();
+				}
 
-			if ( Math.abs( this.vertices[ v1 ].y ) === radius ) {
+				animationCache.prevKey = prevKey;
+				animationCache.nextKey = nextKey;
 
-				uv1.x = ( uv1.x + uv2.x ) / 2;
-				this.faces.push( new THREE.Face3( v1, v3, v4, [ n1, n3, n4 ] ) );
-				this.faceVertexUvs[ 0 ].push( [ uv1, uv3, uv4 ] );
+			}
 
-			} else if ( Math.abs( this.vertices[ v3 ].y ) === radius ) {
+			if ( nextKey.time >= this.currentTime ) {
 
-				uv3.x = ( uv3.x + uv4.x ) / 2;
-				this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
-				this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
+				prevKey.interpolate( nextKey, this.currentTime );
 
 			} else {
 
-				this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
-				this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
-
-				this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) );
-				this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
+				prevKey.interpolate( nextKey, nextKey.time );
 
 			}
 
+			this.data.hierarchy[ h ].node.updateMatrix();
+			object.matrixWorldNeedsUpdate = true;
+
 		}
 
 	}
 
-	this.computeFaceNormals();
-
-	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
-
 };
 
-THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype );
+// Get next key with
 
-// File:src/extras/geometries/TextGeometry.js
+THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) {
 
-/**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- * @author alteredq / http://alteredqualia.com/
- *
- * For creating 3D text geometry in three.js
- *
- * Text = 3D Text
- *
- * parameters = {
- *  size: 			<float>, 	// size of the text
- *  height: 		<float>, 	// thickness to extrude text
- *  curveSegments: 	<int>,		// number of points on the curves
- *
- *  font: 			<string>,		// font name
- *  weight: 		<string>,		// font weight (normal, bold)
- *  style: 			<string>,		// font style  (normal, italics)
- *
- *  bevelEnabled:	<bool>,			// turn on bevel
- *  bevelThickness: <float>, 		// how deep into text bevel goes
- *  bevelSize:		<float>, 		// how far from text outline is bevel
- *  }
- *
- */
+	var keys = this.data.hierarchy[ h ].keys;
+	key = key % keys.length;
 
-/*	Usage Examples
+	for ( ; key < keys.length; key ++ ) {
 
-	// TextGeometry wrapper
+		if ( keys[ key ].hasTarget( sid ) ) {
 
-	var text3d = new TextGeometry( text, options );
+			return keys[ key ];
 
-	// Complete manner
+		}
 
-	var textShapes = THREE.FontUtils.generateShapes( text, options );
-	var text3d = new ExtrudeGeometry( textShapes, options );
+	}
 
-*/
+	return keys[ 0 ];
 
+};
 
-THREE.TextGeometry = function ( text, parameters ) {
+// Get previous key with
 
-	parameters = parameters || {};
+THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) {
 
-	var textShapes = THREE.FontUtils.generateShapes( text, parameters );
+	var keys = this.data.hierarchy[ h ].keys;
+	key = key >= 0 ? key : key + keys.length;
 
-	// translate parameters to ExtrudeGeometry API
+	for ( ; key >= 0; key -- ) {
 
-	parameters.amount = parameters.height !== undefined ? parameters.height : 50;
+		if ( keys[ key ].hasTarget( sid ) ) {
 
-	// defaults
+			return keys[ key ];
 
-	if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
-	if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
-	if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
+		}
 
-	THREE.ExtrudeGeometry.call( this, textShapes, parameters );
+	}
 
-	this.type = 'TextGeometry';
+	return keys[ keys.length - 1 ];
 
 };
 
-THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype );
-
-// File:src/extras/geometries/TorusGeometry.js
+// File:src/extras/animation/MorphAnimation.js
 
 /**
- * @author oosmoxiecode
- * @author mrdoob / http://mrdoob.com/
- * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888
+ * @author mrdoob / http://mrdoob.com
  */
 
-THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) {
-
-	THREE.Geometry.call( this );
-
-	this.type = 'TorusGeometry';
-
-	this.parameters = {
-		radius: radius,
-		tube: tube,
-		radialSegments: radialSegments,
-		tubularSegments: tubularSegments,
-		arc: arc
-	};
-
-	radius = radius || 100;
-	tube = tube || 40;
-	radialSegments = radialSegments || 8;
-	tubularSegments = tubularSegments || 6;
-	arc = arc || Math.PI * 2;
-
-	var center = new THREE.Vector3(), uvs = [], normals = [];
-
-	for ( var j = 0; j <= radialSegments; j ++ ) {
-
-		for ( var i = 0; i <= tubularSegments; i ++ ) {
-
-			var u = i / tubularSegments * arc;
-			var v = j / radialSegments * Math.PI * 2;
+THREE.MorphAnimation = function ( mesh ) {
 
-			center.x = radius * Math.cos( u );
-			center.y = radius * Math.sin( u );
+	this.mesh = mesh;
+	this.frames = mesh.morphTargetInfluences.length;
+	this.currentTime = 0;
+	this.duration = 1000;
+	this.loop = true;
 
-			var vertex = new THREE.Vector3();
-			vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );
-			vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );
-			vertex.z = tube * Math.sin( v );
+	this.isPlaying = false;
 
-			this.vertices.push( vertex );
+};
 
-			uvs.push( new THREE.Vector2( i / tubularSegments, j / radialSegments ) );
-			normals.push( vertex.clone().sub( center ).normalize() );
+THREE.MorphAnimation.prototype = {
 
-		}
+	play: function () {
 
-	}
+		this.isPlaying = true;
 
-	for ( var j = 1; j <= radialSegments; j ++ ) {
+	},
 
-		for ( var i = 1; i <= tubularSegments; i ++ ) {
+	pause: function () {
 
-			var a = ( tubularSegments + 1 ) * j + i - 1;
-			var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;
-			var c = ( tubularSegments + 1 ) * ( j - 1 ) + i;
-			var d = ( tubularSegments + 1 ) * j + i;
+		this.isPlaying = false;
 
-			var face = new THREE.Face3( a, b, d, [ normals[ a ].clone(), normals[ b ].clone(), normals[ d ].clone() ] );
-			this.faces.push( face );
-			this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] );
+	},
 
-			face = new THREE.Face3( b, c, d, [ normals[ b ].clone(), normals[ c ].clone(), normals[ d ].clone() ] );
-			this.faces.push( face );
-			this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] );
+	update: ( function () {
 
-		}
+		var lastFrame = 0;
+		var currentFrame = 0;
 
-	}
+		return function ( delta ) {
 
-	this.computeFaceNormals();
+			if ( this.isPlaying === false ) return;
 
-};
+			this.currentTime += delta;
 
-THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype );
+			if ( this.loop === true && this.currentTime > this.duration ) {
 
-// File:src/extras/geometries/TorusKnotGeometry.js
+				this.currentTime %= this.duration;
 
-/**
- * @author oosmoxiecode
- * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473
- */
+			}
 
-THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) {
+			this.currentTime = Math.min( this.currentTime, this.duration );
 
-	THREE.Geometry.call( this );
+			var interpolation = this.duration / this.frames;
+			var frame = Math.floor( this.currentTime / interpolation );
 
-	this.type = 'TorusKnotGeometry';
+			if ( frame != currentFrame ) {
 
-	this.parameters = {
-		radius: radius,
-		tube: tube,
-		radialSegments: radialSegments,
-		tubularSegments: tubularSegments,
-		p: p,
-		q: q,
-		heightScale: heightScale
-	};
+				this.mesh.morphTargetInfluences[ lastFrame ] = 0;
+				this.mesh.morphTargetInfluences[ currentFrame ] = 1;
+				this.mesh.morphTargetInfluences[ frame ] = 0;
 
-	radius = radius || 100;
-	tube = tube || 40;
-	radialSegments = radialSegments || 64;
-	tubularSegments = tubularSegments || 8;
-	p = p || 2;
-	q = q || 3;
-	heightScale = heightScale || 1;
-	
-	var grid = new Array( radialSegments );
-	var tang = new THREE.Vector3();
-	var n = new THREE.Vector3();
-	var bitan = new THREE.Vector3();
+				lastFrame = currentFrame;
+				currentFrame = frame;
 
-	for ( var i = 0; i < radialSegments; ++ i ) {
+			}
 
-		grid[ i ] = new Array( tubularSegments );
-		var u = i / radialSegments * 2 * p * Math.PI;
-		var p1 = getPos( u, q, p, radius, heightScale );
-		var p2 = getPos( u + 0.01, q, p, radius, heightScale );
-		tang.subVectors( p2, p1 );
-		n.addVectors( p2, p1 );
+			this.mesh.morphTargetInfluences[ frame ] = ( this.currentTime % interpolation ) / interpolation;
+			this.mesh.morphTargetInfluences[ lastFrame ] = 1 - this.mesh.morphTargetInfluences[ frame ];
 
-		bitan.crossVectors( tang, n );
-		n.crossVectors( bitan, tang );
-		bitan.normalize();
-		n.normalize();
+		}
 
-		for ( var j = 0; j < tubularSegments; ++ j ) {
+	} )()
 
-			var v = j / tubularSegments * 2 * Math.PI;
-			var cx = - tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
-			var cy = tube * Math.sin( v );
+};
 
-			var pos = new THREE.Vector3();
-			pos.x = p1.x + cx * n.x + cy * bitan.x;
-			pos.y = p1.y + cx * n.y + cy * bitan.y;
-			pos.z = p1.z + cx * n.z + cy * bitan.z;
+// File:src/extras/geometries/BoxGeometry.js
 
-			grid[ i ][ j ] = this.vertices.push( pos ) - 1;
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as
+ */
 
-		}
+THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
 
-	}
+	THREE.Geometry.call( this );
 
-	for ( var i = 0; i < radialSegments; ++ i ) {
+	this.type = 'BoxGeometry';
 
-		for ( var j = 0; j < tubularSegments; ++ j ) {
+	this.parameters = {
+		width: width,
+		height: height,
+		depth: depth,
+		widthSegments: widthSegments,
+		heightSegments: heightSegments,
+		depthSegments: depthSegments
+	};
 
-			var ip = ( i + 1 ) % radialSegments;
-			var jp = ( j + 1 ) % tubularSegments;
+	this.widthSegments = widthSegments || 1;
+	this.heightSegments = heightSegments || 1;
+	this.depthSegments = depthSegments || 1;
 
-			var a = grid[ i ][ j ];
-			var b = grid[ ip ][ j ];
-			var c = grid[ ip ][ jp ];
-			var d = grid[ i ][ jp ];
+	var scope = this;
 
-			var uva = new THREE.Vector2( i / radialSegments, j / tubularSegments );
-			var uvb = new THREE.Vector2( ( i + 1 ) / radialSegments, j / tubularSegments );
-			var uvc = new THREE.Vector2( ( i + 1 ) / radialSegments, ( j + 1 ) / tubularSegments );
-			var uvd = new THREE.Vector2( i / radialSegments, ( j + 1 ) / tubularSegments );
+	var width_half = width / 2;
+	var height_half = height / 2;
+	var depth_half = depth / 2;
 
-			this.faces.push( new THREE.Face3( a, b, d ) );
-			this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
+	buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, 0 ); // px
+	buildPlane( 'z', 'y',   1, - 1, depth, height, - width_half, 1 ); // nx
+	buildPlane( 'x', 'z',   1,   1, width, depth, height_half, 2 ); // py
+	buildPlane( 'x', 'z',   1, - 1, width, depth, - height_half, 3 ); // ny
+	buildPlane( 'x', 'y',   1, - 1, width, height, depth_half, 4 ); // pz
+	buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, 5 ); // nz
 
-			this.faces.push( new THREE.Face3( b, c, d ) );
-			this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
+	function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) {
 
-		}
-	}
+		var w, ix, iy,
+		gridX = scope.widthSegments,
+		gridY = scope.heightSegments,
+		width_half = width / 2,
+		height_half = height / 2,
+		offset = scope.vertices.length;
 
-	this.computeFaceNormals();
-	this.computeVertexNormals();
+		if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) {
 
-	function getPos( u, in_q, in_p, radius, heightScale ) {
+			w = 'z';
 
-		var cu = Math.cos( u );
-		var su = Math.sin( u );
-		var quOverP = in_q / in_p * u;
-		var cs = Math.cos( quOverP );
+		} else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) {
 
-		var tx = radius * ( 2 + cs ) * 0.5 * cu;
-		var ty = radius * ( 2 + cs ) * su * 0.5;
-		var tz = heightScale * radius * Math.sin( quOverP ) * 0.5;
+			w = 'y';
+			gridY = scope.depthSegments;
 
-		return new THREE.Vector3( tx, ty, tz );
+		} else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {
 
-	}
+			w = 'x';
+			gridX = scope.depthSegments;
 
-};
+		}
 
-THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype );
+		var gridX1 = gridX + 1,
+		gridY1 = gridY + 1,
+		segment_width = width / gridX,
+		segment_height = height / gridY,
+		normal = new THREE.Vector3();
 
-// File:src/extras/geometries/TubeGeometry.js
+		normal[ w ] = depth > 0 ? 1 : - 1;
 
-/**
- * @author WestLangley / https://github.com/WestLangley
- * @author zz85 / https://github.com/zz85
- * @author miningold / https://github.com/miningold
- *
- * Modified from the TorusKnotGeometry by @oosmoxiecode
- *
- * Creates a tube which extrudes along a 3d spline
- *
- * Uses parallel transport frames as described in
- * http://www.cs.indiana.edu/pub/techreports/TR425.pdf
- */
+		for ( iy = 0; iy < gridY1; iy ++ ) {
 
-THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed ) {
+			for ( ix = 0; ix < gridX1; ix ++ ) {
 
-	THREE.Geometry.call( this );
+				var vector = new THREE.Vector3();
+				vector[ u ] = ( ix * segment_width - width_half ) * udir;
+				vector[ v ] = ( iy * segment_height - height_half ) * vdir;
+				vector[ w ] = depth;
 
-	this.type = 'TubeGeometry';
+				scope.vertices.push( vector );
 
-	this.parameters = {
-		path: path,
-		segments: segments,
-		radius: radius,
-		radialSegments: radialSegments,
-		closed: closed
-	};
+			}
 
-	segments = segments || 64;
-	radius = radius || 1;
-	radialSegments = radialSegments || 8;
-	closed = closed || false;
+		}
 
-	var grid = [];
+		for ( iy = 0; iy < gridY; iy ++ ) {
 
-	var scope = this,
+			for ( ix = 0; ix < gridX; ix ++ ) {
 
-		tangent,
-		normal,
-		binormal,
+				var a = ix + gridX1 * iy;
+				var b = ix + gridX1 * ( iy + 1 );
+				var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
+				var d = ( ix + 1 ) + gridX1 * iy;
 
-		numpoints = segments + 1,
+				var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY );
+				var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY );
+				var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY );
+				var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY );
 
-		x, y, z,
-		tx, ty, tz,
-		u, v,
+				var face = new THREE.Face3( a + offset, b + offset, d + offset );
+				face.normal.copy( normal );
+				face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
+				face.materialIndex = materialIndex;
 
-		cx, cy,
-		pos, pos2 = new THREE.Vector3(),
-		i, j,
-		ip, jp,
-		a, b, c, d,
-		uva, uvb, uvc, uvd;
+				scope.faces.push( face );
+				scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
 
-	var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ),
-		tangents = frames.tangents,
-		normals = frames.normals,
-		binormals = frames.binormals;
+				face = new THREE.Face3( b + offset, c + offset, d + offset );
+				face.normal.copy( normal );
+				face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
+				face.materialIndex = materialIndex;
 
-	// proxy internals
-	this.tangents = tangents;
-	this.normals = normals;
-	this.binormals = binormals;
+				scope.faces.push( face );
+				scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
 
-	function vert( x, y, z ) {
+			}
 
-		return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1;
+		}
 
 	}
 
-	// consruct the grid
+	this.mergeVertices();
 
-	for ( i = 0; i < numpoints; i ++ ) {
+};
 
-		grid[ i ] = [];
+THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-		u = i / ( numpoints - 1 );
+// File:src/extras/geometries/CircleGeometry.js
 
-		pos = path.getPointAt( u );
+/**
+ * @author hughes
+ */
 
-		tangent = tangents[ i ];
-		normal = normals[ i ];
-		binormal = binormals[ i ];
+THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) {
 
-		for ( j = 0; j < radialSegments; j ++ ) {
+	THREE.Geometry.call( this );
 
-			v = j / radialSegments * 2 * Math.PI;
+	this.type = 'CircleGeometry';
 
-			cx = - radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
-			cy = radius * Math.sin( v );
+	this.parameters = {
+		radius: radius,
+		segments: segments,
+		thetaStart: thetaStart,
+		thetaLength: thetaLength
+	};
 
-			pos2.copy( pos );
-			pos2.x += cx * normal.x + cy * binormal.x;
-			pos2.y += cx * normal.y + cy * binormal.y;
-			pos2.z += cx * normal.z + cy * binormal.z;
+	radius = radius || 50;
+	segments = segments !== undefined ? Math.max( 3, segments ) : 8;
 
-			grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );
+	thetaStart = thetaStart !== undefined ? thetaStart : 0;
+	thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
 
-		}
-	}
+	var i, uvs = [],
+	center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 );
 
+	this.vertices.push(center);
+	uvs.push( centerUV );
 
-	// construct the mesh
+	for ( i = 0; i <= segments; i ++ ) {
 
-	for ( i = 0; i < segments; i ++ ) {
+		var vertex = new THREE.Vector3();
+		var segment = thetaStart + i / segments * thetaLength;
 
-		for ( j = 0; j < radialSegments; j ++ ) {
+		vertex.x = radius * Math.cos( segment );
+		vertex.y = radius * Math.sin( segment );
 
-			ip = ( closed ) ? (i + 1) % segments : i + 1;
-			jp = (j + 1) % radialSegments;
+		this.vertices.push( vertex );
+		uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, ( vertex.y / radius + 1 ) / 2 ) );
 
-			a = grid[ i ][ j ];		// *** NOT NECESSARILY PLANAR ! ***
-			b = grid[ ip ][ j ];
-			c = grid[ ip ][ jp ];
-			d = grid[ i ][ jp ];
+	}
 
-			uva = new THREE.Vector2( i / segments, j / radialSegments );
-			uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments );
-			uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments );
-			uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments );
+	var n = new THREE.Vector3( 0, 0, 1 );
 
-			this.faces.push( new THREE.Face3( a, b, d ) );
-			this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
+	for ( i = 1; i <= segments; i ++ ) {
 
-			this.faces.push( new THREE.Face3( b, c, d ) );
-			this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
+		this.faces.push( new THREE.Face3( i, i + 1, 0, [ n.clone(), n.clone(), n.clone() ] ) );
+		this.faceVertexUvs[ 0 ].push( [ uvs[ i ].clone(), uvs[ i + 1 ].clone(), centerUV.clone() ] );
 
-		}
 	}
 
 	this.computeFaceNormals();
-	this.computeVertexNormals();
+
+	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
 
 };
 
-THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
+// File:src/extras/geometries/CubeGeometry.js
 
-// For computing of Frenet frames, exposing the tangents, normals and binormals the spline
-THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-	var	tangent = new THREE.Vector3(),
-		normal = new THREE.Vector3(),
-		binormal = new THREE.Vector3(),
 
-		tangents = [],
-		normals = [],
-		binormals = [],
+THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
 
-		vec = new THREE.Vector3(),
-		mat = new THREE.Matrix4(),
+	console.warn( 'THREE.CubeGeometry has been renamed to THREE.BoxGeometry.' );
+	return new THREE.BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments );
 
-		numpoints = segments + 1,
-		theta,
-		epsilon = 0.0001,
-		smallest,
+ };
 
-		tx, ty, tz,
-		i, u, v;
+// File:src/extras/geometries/CylinderGeometry.js
 
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-	// expose internals
-	this.tangents = tangents;
-	this.normals = normals;
-	this.binormals = binormals;
+THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded ) {
 
-	// compute the tangent vectors for each segment on the path
+	THREE.Geometry.call( this );
 
-	for ( i = 0; i < numpoints; i ++ ) {
+	this.type = 'CylinderGeometry';
 
-		u = i / ( numpoints - 1 );
+	this.parameters = {
+		radiusTop: radiusTop,
+		radiusBottom: radiusBottom,
+		height: height,
+		radialSegments: radialSegments,
+		heightSegments: heightSegments,
+		openEnded: openEnded
+	};
 
-		tangents[ i ] = path.getTangentAt( u );
-		tangents[ i ].normalize();
+	radiusTop = radiusTop !== undefined ? radiusTop : 20;
+	radiusBottom = radiusBottom !== undefined ? radiusBottom : 20;
+	height = height !== undefined ? height : 100;
 
-	}
+	radialSegments = radialSegments || 8;
+	heightSegments = heightSegments || 1;
 
-	initialNormal3();
+	openEnded = openEnded !== undefined ? openEnded : false;
 
-	/*
-	function initialNormal1(lastBinormal) {
-		// fixed start binormal. Has dangers of 0 vectors
-		normals[ 0 ] = new THREE.Vector3();
-		binormals[ 0 ] = new THREE.Vector3();
-		if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 );
-		normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize();
-		binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
-	}
+	var heightHalf = height / 2;
 
-	function initialNormal2() {
+	var x, y, vertices = [], uvs = [];
 
-		// This uses the Frenet-Serret formula for deriving binormal
-		var t2 = path.getTangentAt( epsilon );
+	for ( y = 0; y <= heightSegments; y ++ ) {
 
-		normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize();
-		binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] );
+		var verticesRow = [];
+		var uvsRow = [];
 
-		normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent
-		binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
+		var v = y / heightSegments;
+		var radius = v * ( radiusBottom - radiusTop ) + radiusTop;
 
-	}
-	*/
+		for ( x = 0; x <= radialSegments; x ++ ) {
 
-	function initialNormal3() {
-		// select an initial normal vector perpenicular to the first tangent vector,
-		// and in the direction of the smallest tangent xyz component
+			var u = x / radialSegments;
 
-		normals[ 0 ] = new THREE.Vector3();
-		binormals[ 0 ] = new THREE.Vector3();
-		smallest = Number.MAX_VALUE;
-		tx = Math.abs( tangents[ 0 ].x );
-		ty = Math.abs( tangents[ 0 ].y );
-		tz = Math.abs( tangents[ 0 ].z );
+			var vertex = new THREE.Vector3();
+			vertex.x = radius * Math.sin( u * Math.PI * 2 );
+			vertex.y = - v * height + heightHalf;
+			vertex.z = radius * Math.cos( u * Math.PI * 2 );
 
-		if ( tx <= smallest ) {
-			smallest = tx;
-			normal.set( 1, 0, 0 );
-		}
+			this.vertices.push( vertex );
 
-		if ( ty <= smallest ) {
-			smallest = ty;
-			normal.set( 0, 1, 0 );
-		}
+			verticesRow.push( this.vertices.length - 1 );
+			uvsRow.push( new THREE.Vector2( u, 1 - v ) );
 
-		if ( tz <= smallest ) {
-			normal.set( 0, 0, 1 );
 		}
 
-		vec.crossVectors( tangents[ 0 ], normal ).normalize();
+		vertices.push( verticesRow );
+		uvs.push( uvsRow );
 
-		normals[ 0 ].crossVectors( tangents[ 0 ], vec );
-		binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );
 	}
 
+	var tanTheta = ( radiusBottom - radiusTop ) / height;
+	var na, nb;
 
-	// compute the slowly-varying normal and binormal vectors for each segment on the path
+	for ( x = 0; x < radialSegments; x ++ ) {
 
-	for ( i = 1; i < numpoints; i ++ ) {
+		if ( radiusTop !== 0 ) {
 
-		normals[ i ] = normals[ i-1 ].clone();
+			na = this.vertices[ vertices[ 0 ][ x ] ].clone();
+			nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone();
 
-		binormals[ i ] = binormals[ i-1 ].clone();
+		} else {
 
-		vec.crossVectors( tangents[ i-1 ], tangents[ i ] );
+			na = this.vertices[ vertices[ 1 ][ x ] ].clone();
+			nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone();
 
-		if ( vec.length() > epsilon ) {
+		}
 
-			vec.normalize();
+		na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize();
+		nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize();
 
-			theta = Math.acos( THREE.Math.clamp( tangents[ i-1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors
+		for ( y = 0; y < heightSegments; y ++ ) {
 
-			normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
+			var v1 = vertices[ y ][ x ];
+			var v2 = vertices[ y + 1 ][ x ];
+			var v3 = vertices[ y + 1 ][ x + 1 ];
+			var v4 = vertices[ y ][ x + 1 ];
 
-		}
+			var n1 = na.clone();
+			var n2 = na.clone();
+			var n3 = nb.clone();
+			var n4 = nb.clone();
 
-		binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
+			var uv1 = uvs[ y ][ x ].clone();
+			var uv2 = uvs[ y + 1 ][ x ].clone();
+			var uv3 = uvs[ y + 1 ][ x + 1 ].clone();
+			var uv4 = uvs[ y ][ x + 1 ].clone();
+
+			this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
+
+			this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
+
+		}
 
 	}
 
+	// top cap
 
-	// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
+	if ( openEnded === false && radiusTop > 0 ) {
 
-	if ( closed ) {
+		this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) );
 
-		theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints-1 ] ), - 1, 1 ) );
-		theta /= ( numpoints - 1 );
+		for ( x = 0; x < radialSegments; x ++ ) {
 
-		if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) {
+			var v1 = vertices[ 0 ][ x ];
+			var v2 = vertices[ 0 ][ x + 1 ];
+			var v3 = this.vertices.length - 1;
 
-			theta = - theta;
+			var n1 = new THREE.Vector3( 0, 1, 0 );
+			var n2 = new THREE.Vector3( 0, 1, 0 );
+			var n3 = new THREE.Vector3( 0, 1, 0 );
+
+			var uv1 = uvs[ 0 ][ x ].clone();
+			var uv2 = uvs[ 0 ][ x + 1 ].clone();
+			var uv3 = new THREE.Vector2( uv2.x, 0 );
+
+			this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
 
 		}
 
-		for ( i = 1; i < numpoints; i ++ ) {
+	}
 
-			// twist a little...
-			normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
-			binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
+	// bottom cap
+
+	if ( openEnded === false && radiusBottom > 0 ) {
+
+		this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) );
+
+		for ( x = 0; x < radialSegments; x ++ ) {
+
+			var v1 = vertices[ y ][ x + 1 ];
+			var v2 = vertices[ y ][ x ];
+			var v3 = this.vertices.length - 1;
+
+			var n1 = new THREE.Vector3( 0, - 1, 0 );
+			var n2 = new THREE.Vector3( 0, - 1, 0 );
+			var n3 = new THREE.Vector3( 0, - 1, 0 );
+
+			var uv1 = uvs[ y ][ x + 1 ].clone();
+			var uv2 = uvs[ y ][ x ].clone();
+			var uv3 = new THREE.Vector2( uv2.x, 1 );
+
+			this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
 
 		}
 
 	}
-};
 
-// File:src/extras/geometries/PolyhedronGeometry.js
+	this.computeFaceNormals();
 
-/**
- * @author clockworkgeek / https://github.com/clockworkgeek
- * @author timothypratley / https://github.com/timothypratley
- * @author WestLangley / http://github.com/WestLangley
-*/
+}
 
-THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) {
+THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+// File:src/extras/geometries/ExtrudeGeometry.js
+
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ *
+ * Creates extruded geometry from a path shape.
+ *
+ * parameters = {
+ *
+ *  curveSegments: <int>, // number of points on the curves
+ *  steps: <int>, // number of points for z-side extrusions / used for subdividing segements of extrude spline too
+ *  amount: <int>, // Depth to extrude the shape
+ *
+ *  bevelEnabled: <bool>, // turn on bevel
+ *  bevelThickness: <float>, // how deep into the original shape bevel goes
+ *  bevelSize: <float>, // how far from shape outline is bevel
+ *  bevelSegments: <int>, // number of bevel layers
+ *
+ *  extrudePath: <THREE.CurvePath> // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined)
+ *  frames: <THREE.TubeGeometry.FrenetFrames> // containing arrays of tangents, normals, binormals
+ *
+ *  material: <int> // material index for front and back faces
+ *  extrudeMaterial: <int> // material index for extrusion and beveled faces
+ *  uvGenerator: <Object> // object that provides UV generator functions
+ *
+ * }
+ **/
 
-	THREE.Geometry.call( this );
+THREE.ExtrudeGeometry = function ( shapes, options ) {
 
-	this.type = 'PolyhedronGeometry';
+	if ( typeof( shapes ) === "undefined" ) {
+		shapes = [];
+		return;
+	}
 
-	this.parameters = {
-		vertices: vertices,
-		indices: indices,
-		radius: radius,
-		detail: detail
-	};
+	THREE.Geometry.call( this );
 
-	radius = radius || 1;
-	detail = detail || 0;
+	this.type = 'ExtrudeGeometry';
 
-	var that = this;
+	shapes = shapes instanceof Array ? shapes : [ shapes ];
 
-	for ( var i = 0, l = vertices.length; i < l; i += 3 ) {
+	this.addShapeList( shapes, options );
 
-		prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) );
+	this.computeFaceNormals();
 
-	}
+	// can't really use automatic vertex normals
+	// as then front and back sides get smoothed too
+	// should do separate smoothing just for sides
 
-	var midpoints = [], p = this.vertices;
+	//this.computeVertexNormals();
 
-	var faces = [];
+	//console.log( "took", ( Date.now() - startTime ) );
 
-	for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) {
+};
 
-		var v1 = p[ indices[ i     ] ];
-		var v2 = p[ indices[ i + 1 ] ];
-		var v3 = p[ indices[ i + 2 ] ];
+THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-		faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] );
+THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) {
+	var sl = shapes.length;
 
+	for ( var s = 0; s < sl; s ++ ) {
+		var shape = shapes[ s ];
+		this.addShape( shape, options );
 	}
+};
 
-	var centroid = new THREE.Vector3();
-
-	for ( var i = 0, l = faces.length; i < l; i ++ ) {
+THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) {
 
-		subdivide( faces[ i ], detail );
+	var amount = options.amount !== undefined ? options.amount : 100;
 
-	}
+	var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10
+	var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8
+	var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
 
+	var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
 
-	// Handle case when face straddles the seam
+	var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
 
-	for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) {
+	var steps = options.steps !== undefined ? options.steps : 1;
 
-		var uvs = this.faceVertexUvs[ 0 ][ i ];
+	var extrudePath = options.extrudePath;
+	var extrudePts, extrudeByPath = false;
 
-		var x0 = uvs[ 0 ].x;
-		var x1 = uvs[ 1 ].x;
-		var x2 = uvs[ 2 ].x;
+	var material = options.material;
+	var extrudeMaterial = options.extrudeMaterial;
 
-		var max = Math.max( x0, Math.max( x1, x2 ) );
-		var min = Math.min( x0, Math.min( x1, x2 ) );
+	// Use default WorldUVGenerator if no UV generators are specified.
+	var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator;
 
-		if ( max > 0.9 && min < 0.1 ) { // 0.9 is somewhat arbitrary
+	var splineTube, binormal, normal, position2;
+	if ( extrudePath ) {
 
-			if ( x0 < 0.2 ) uvs[ 0 ].x += 1;
-			if ( x1 < 0.2 ) uvs[ 1 ].x += 1;
-			if ( x2 < 0.2 ) uvs[ 2 ].x += 1;
+		extrudePts = extrudePath.getSpacedPoints( steps );
 
-		}
+		extrudeByPath = true;
+		bevelEnabled = false; // bevels not supported for path extrusion
 
-	}
+		// SETUP TNB variables
 
+		// Reuse TNB from TubeGeomtry for now.
+		// TODO1 - have a .isClosed in spline?
 
-	// Apply radius
+		splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false);
 
-	for ( var i = 0, l = this.vertices.length; i < l; i ++ ) {
+		// console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
 
-		this.vertices[ i ].multiplyScalar( radius );
+		binormal = new THREE.Vector3();
+		normal = new THREE.Vector3();
+		position2 = new THREE.Vector3();
 
 	}
 
+	// Safeguards if bevels are not enabled
 
-	// Merge vertices
+	if ( ! bevelEnabled ) {
 
-	this.mergeVertices();
+		bevelSegments = 0;
+		bevelThickness = 0;
+		bevelSize = 0;
 
-	this.computeFaceNormals();
+	}
 
-	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+	// Variables initalization
 
+	var ahole, h, hl; // looping of holes
+	var scope = this;
+	var bevelPoints = [];
 
-	// Project vector onto sphere's surface
+	var shapesOffset = this.vertices.length;
 
-	function prepare( vector ) {
+	var shapePoints = shape.extractPoints( curveSegments );
 
-		var vertex = vector.normalize().clone();
-		vertex.index = that.vertices.push( vertex ) - 1;
+	var vertices = shapePoints.shape;
+	var holes = shapePoints.holes;
 
-		// Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle.
+	var reverse = ! THREE.Shape.Utils.isClockWise( vertices ) ;
 
-		var u = azimuth( vector ) / 2 / Math.PI + 0.5;
-		var v = inclination( vector ) / Math.PI + 0.5;
-		vertex.uv = new THREE.Vector2( u, 1 - v );
+	if ( reverse ) {
 
-		return vertex;
+		vertices = vertices.reverse();
 
-	}
+		// Maybe we should also check if holes are in the opposite direction, just to be safe ...
 
+		for ( h = 0, hl = holes.length; h < hl; h ++ ) {
 
-	// Approximate a curved face with recursively sub-divided triangles.
+			ahole = holes[ h ];
 
-	function make( v1, v2, v3 ) {
+			if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
 
-		var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] );
-		that.faces.push( face );
+				holes[ h ] = ahole.reverse();
 
-		centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 );
+			}
 
-		var azi = azimuth( centroid );
+		}
 
-		that.faceVertexUvs[ 0 ].push( [
-			correctUV( v1.uv, v1, azi ),
-			correctUV( v2.uv, v2, azi ),
-			correctUV( v3.uv, v3, azi )
-		] );
+		reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
 
 	}
 
 
-	// Analytically subdivide a face to the required detail level.
-
-	function subdivide( face, detail ) {
-
-		var cols = Math.pow(2, detail);
-		var cells = Math.pow(4, detail);
-		var a = prepare( that.vertices[ face.a ] );
-		var b = prepare( that.vertices[ face.b ] );
-		var c = prepare( that.vertices[ face.c ] );
-		var v = [];
+	var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
 
-		// Construct all of the vertices for this subdivision.
+	/* Vertices */
 
-		for ( var i = 0 ; i <= cols; i ++ ) {
+	var contour = vertices; // vertices has all points but contour has only points of circumference
 
-			v[ i ] = [];
+	for ( h = 0, hl = holes.length;  h < hl; h ++ ) {
 
-			var aj = prepare( a.clone().lerp( c, i / cols ) );
-			var bj = prepare( b.clone().lerp( c, i / cols ) );
-			var rows = cols - i;
+		ahole = holes[ h ];
 
-			for ( var j = 0; j <= rows; j ++) {
+		vertices = vertices.concat( ahole );
 
-				if ( j == 0 && i == cols ) {
+	}
 
-					v[ i ][ j ] = aj;
 
-				} else {
+	function scalePt2 ( pt, vec, size ) {
 
-					v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) );
+		if ( ! vec ) console.log( "die" );
 
-				}
+		return vec.clone().multiplyScalar( size ).add( pt );
 
-			}
+	}
 
-		}
+	var b, bs, t, z,
+		vert, vlen = vertices.length,
+		face, flen = faces.length,
+		cont, clen = contour.length;
 
-		// Construct all of the faces.
 
-		for ( var i = 0; i < cols ; i ++ ) {
+	// Find directions for point movement
 
-			for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) {
+	var RAD_TO_DEGREES = 180 / Math.PI;
 
-				var k = Math.floor( j / 2 );
 
-				if ( j % 2 == 0 ) {
+	function getBevelVec( inPt, inPrev, inNext ) {
 
-					make(
-						v[ i ][ k + 1],
-						v[ i + 1 ][ k ],
-						v[ i ][ k ]
-					);
+		var EPSILON = 0.0000000001;
+		
+		// computes for inPt the corresponding point inPt' on a new contour
+		//   shiftet by 1 unit (length of normalized vector) to the left
+		// if we walk along contour clockwise, this new contour is outside the old one
+		//
+		// inPt' is the intersection of the two lines parallel to the two
+		//  adjacent edges of inPt at a distance of 1 unit on the left side.
+		
+		var v_trans_x, v_trans_y, shrink_by = 1;		// resulting translation vector for inPt
 
-				} else {
+		// good reading for geometry algorithms (here: line-line intersection)
+		// http://geomalgorithms.com/a05-_intersect-1.html
 
-					make(
-						v[ i ][ k + 1 ],
-						v[ i + 1][ k + 1],
-						v[ i + 1 ][ k ]
-					);
+		var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y;
+		var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y;
+		
+		var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );
+		
+		// check for colinear edges
+		var colinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );
+		
+		if ( Math.abs( colinear0 ) > EPSILON ) {		// not colinear
+			
+			// length of vectors for normalizing
+	
+			var v_prev_len = Math.sqrt( v_prev_lensq );
+			var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );
+			
+			// shift adjacent points by unit vectors to the left
+	
+			var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );
+			var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );
+			
+			var ptNextShift_x = ( inNext.x - v_next_y / v_next_len );
+			var ptNextShift_y = ( inNext.y + v_next_x / v_next_len );
+	
+			// scaling factor for v_prev to intersection point
+	
+			var sf = (  ( ptNextShift_x - ptPrevShift_x ) * v_next_y -
+						( ptNextShift_y - ptPrevShift_y ) * v_next_x    ) /
+					  ( v_prev_x * v_next_y - v_prev_y * v_next_x );
+	
+			// vector from inPt to intersection point
+	
+			v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );
+			v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );
+	
+			// Don't normalize!, otherwise sharp corners become ugly
+			//  but prevent crazy spikes
+			var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y )
+			if ( v_trans_lensq <= 2 ) {
+				return	new THREE.Vector2( v_trans_x, v_trans_y );
+			} else {
+				shrink_by = Math.sqrt( v_trans_lensq / 2 );
+			}
+			
+		} else {		// handle special case of colinear edges
 
+			var direction_eq = false;		// assumes: opposite
+			if ( v_prev_x > EPSILON ) {
+				if ( v_next_x > EPSILON ) { direction_eq = true; }
+			} else {
+				if ( v_prev_x < - EPSILON ) {
+					if ( v_next_x < - EPSILON ) { direction_eq = true; }
+				} else {
+					if ( Math.sign(v_prev_y) == Math.sign(v_next_y) ) { direction_eq = true; }
 				}
+			}
 
+			if ( direction_eq ) {
+				// console.log("Warning: lines are a straight sequence");
+				v_trans_x = - v_prev_y;
+				v_trans_y =  v_prev_x;
+				shrink_by = Math.sqrt( v_prev_lensq );
+			} else {
+				// console.log("Warning: lines are a straight spike");
+				v_trans_x = v_prev_x;
+				v_trans_y = v_prev_y;
+				shrink_by = Math.sqrt( v_prev_lensq / 2 );
 			}
 
 		}
 
-	}
-
-
-	// Angle around the Y axis, counter-clockwise when looking from above.
-
-	function azimuth( vector ) {
-
-		return Math.atan2( vector.z, - vector.x );
+		return	new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );
 
 	}
 
 
-	// Angle above the XZ plane.
-
-	function inclination( vector ) {
-
-		return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );
+	var contourMovements = [];
 
-	}
+	for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
 
+		if ( j === il ) j = 0;
+		if ( k === il ) k = 0;
 
-	// Texture fixing helper. Spheres have some odd behaviours.
+		//  (j)---(i)---(k)
+		// console.log('i,j,k', i, j , k)
 
-	function correctUV( uv, vector, azimuth ) {
+		var pt_i = contour[ i ];
+		var pt_j = contour[ j ];
+		var pt_k = contour[ k ];
 
-		if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y );
-		if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y );
-		return uv.clone();
+		contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
 
 	}
 
+	var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
 
-};
-
-THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
-
-// File:src/extras/geometries/DodecahedronGeometry.js
-
-/**
- * @author Abe Pazos / https://hamoid.com
- */
-
-THREE.DodecahedronGeometry = function ( radius, detail ) {
-
-	this.parameters = {
-		radius: radius,
-		detail: detail
-	};
-
-	var t = ( 1 + Math.sqrt( 5 ) ) / 2;
-	var r = 1 / t;
-
-	var vertices = [
-
-		// (±1, ±1, ±1)
-		-1, -1, -1,    -1, -1,  1,
-		-1,  1, -1,    -1,  1,  1,
-		 1, -1, -1,     1, -1,  1,
-		 1,  1, -1,     1,  1,  1,
-
-		// (0, ±1/φ, ±φ)
-		 0, -r, -t,     0, -r,  t,
-		 0,  r, -t,     0,  r,  t,
-
-		// (±1/φ, ±φ, 0)
-		-r, -t,  0,    -r,  t,  0,
-		 r, -t,  0,     r,  t,  0,
-
-		// (±φ, 0, ±1/φ)
-		-t,  0, -r,     t,  0, -r,
-		-t,  0,  r,     t,  0,  r
-	];
-
-	var indices = [
-		 3, 11,  7,      3,  7, 15,      3, 15, 13,
-		 7, 19, 17,      7, 17,  6,      7,  6, 15,
-		17,  4,  8,     17,  8, 10,     17, 10,  6,
-		 8,  0, 16,      8, 16,  2,      8,  2, 10,
-		 0, 12,  1,      0,  1, 18,      0, 18, 16,
-		 6, 10,  2,      6,  2, 13,      6, 13, 15,
-		 2, 16, 18,      2, 18,  3,      2,  3, 13,
-		18,  1,  9,     18,  9, 11,     18, 11,  3,
-		 4, 14, 12,      4, 12,  0,      4,  0,  8,
-		11,  9,  5,     11,  5, 19,     11, 19,  7,
-		19,  5, 14,     19, 14,  4,     19,  4, 17,
-		 1, 12, 14,      1, 14,  5,      1,  5,  9
-	];
-
-	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
-
-};
-
-THREE.DodecahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
-
-// File:src/extras/geometries/IcosahedronGeometry.js
-
-/**
- * @author timothypratley / https://github.com/timothypratley
- */
-
-THREE.IcosahedronGeometry = function ( radius, detail ) {
-
-	var t = ( 1 + Math.sqrt( 5 ) ) / 2;
-
-	var vertices = [
-		- 1,  t,  0,    1,  t,  0,   - 1, - t,  0,    1, - t,  0,
-		 0, - 1,  t,    0,  1,  t,    0, - 1, - t,    0,  1, - t,
-		 t,  0, - 1,    t,  0,  1,   - t,  0, - 1,   - t,  0,  1
-	];
-
-	var indices = [
-		 0, 11,  5,    0,  5,  1,    0,  1,  7,    0,  7, 10,    0, 10, 11,
-		 1,  5,  9,    5, 11,  4,   11, 10,  2,   10,  7,  6,    7,  1,  8,
-		 3,  9,  4,    3,  4,  2,    3,  2,  6,    3,  6,  8,    3,  8,  9,
-		 4,  9,  5,    2,  4, 11,    6,  2, 10,    8,  6,  7,    9,  8,  1
-	];
+	for ( h = 0, hl = holes.length; h < hl; h ++ ) {
 
-	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
+		ahole = holes[ h ];
 
-	this.type = 'IcosahedronGeometry';
+		oneHoleMovements = [];
 
-	this.parameters = {
-		radius: radius,
-		detail: detail
-	};
-};
+		for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
 
-THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+			if ( j === il ) j = 0;
+			if ( k === il ) k = 0;
 
-// File:src/extras/geometries/OctahedronGeometry.js
+			//  (j)---(i)---(k)
+			oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
 
-/**
- * @author timothypratley / https://github.com/timothypratley
- */
+		}
 
-THREE.OctahedronGeometry = function ( radius, detail ) {
+		holesMovements.push( oneHoleMovements );
+		verticesMovements = verticesMovements.concat( oneHoleMovements );
 
-	this.parameters = {
-		radius: radius,
-		detail: detail
-	};
+	}
 
-	var vertices = [
-		1, 0, 0,   - 1, 0, 0,    0, 1, 0,    0,- 1, 0,    0, 0, 1,    0, 0,- 1
-	];
 
-	var indices = [
-		0, 2, 4,    0, 4, 3,    0, 3, 5,    0, 5, 2,    1, 2, 5,    1, 5, 3,    1, 3, 4,    1, 4, 2
-	];
+	// Loop bevelSegments, 1 for the front, 1 for the back
 
-	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
+	for ( b = 0; b < bevelSegments; b ++ ) {
+	//for ( b = bevelSegments; b > 0; b -- ) {
 
-	this.type = 'OctahedronGeometry';
+		t = b / bevelSegments;
+		z = bevelThickness * ( 1 - t );
 
-	this.parameters = {
-		radius: radius,
-		detail: detail
-	};
-};
+		//z = bevelThickness * t;
+		bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved
+		//bs = bevelSize * t ; // linear
 
-THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+		// contract shape
 
-// File:src/extras/geometries/TetrahedronGeometry.js
+		for ( i = 0, il = contour.length; i < il; i ++ ) {
 
-/**
- * @author timothypratley / https://github.com/timothypratley
- */
+			vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
 
-THREE.TetrahedronGeometry = function ( radius, detail ) {
+			v( vert.x, vert.y,  - z );
 
-	var vertices = [
-		 1,  1,  1,   - 1, - 1,  1,   - 1,  1, - 1,    1, - 1, - 1
-	];
+		}
 
-	var indices = [
-		 2,  1,  0,    0,  3,  2,    1,  3,  0,    2,  3,  1
-	];
+		// expand holes
 
-	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
+		for ( h = 0, hl = holes.length; h < hl; h ++ ) {
 
-	this.type = 'TetrahedronGeometry';
+			ahole = holes[ h ];
+			oneHoleMovements = holesMovements[ h ];
 
-	this.parameters = {
-		radius: radius,
-		detail: detail
-	};
+			for ( i = 0, il = ahole.length; i < il; i ++ ) {
 
-};
+				vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
 
-THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+				v( vert.x, vert.y,  - z );
 
-// File:src/extras/geometries/ParametricGeometry.js
+			}
 
-/**
- * @author zz85 / https://github.com/zz85
- * Parametric Surfaces Geometry
- * based on the brilliant article by @prideout http://prideout.net/blog/?p=44
- *
- * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements );
- *
- */
+		}
 
-THREE.ParametricGeometry = function ( func, slices, stacks ) {
+	}
 
-	THREE.Geometry.call( this );
+	bs = bevelSize;
 
-	this.type = 'ParametricGeometry';
+	// Back facing vertices
 
-	this.parameters = {
-		func: func,
-		slices: slices,
-		stacks: stacks
-	};
+	for ( i = 0; i < vlen; i ++ ) {
 
-	var verts = this.vertices;
-	var faces = this.faces;
-	var uvs = this.faceVertexUvs[ 0 ];
+		vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
 
-	var i, il, j, p;
-	var u, v;
+		if ( ! extrudeByPath ) {
 
-	var stackCount = stacks + 1;
-	var sliceCount = slices + 1;
+			v( vert.x, vert.y, 0 );
 
-	for ( i = 0; i <= stacks; i ++ ) {
+		} else {
 
-		v = i / stacks;
+			// v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
 
-		for ( j = 0; j <= slices; j ++ ) {
+			normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x);
+			binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y);
 
-			u = j / slices;
+			position2.copy( extrudePts[0] ).add(normal).add(binormal);
 
-			p = func( u, v );
-			verts.push( p );
+			v( position2.x, position2.y, position2.z );
 
 		}
+
 	}
 
-	var a, b, c, d;
-	var uva, uvb, uvc, uvd;
+	// Add stepped vertices...
+	// Including front facing vertices
 
-	for ( i = 0; i < stacks; i ++ ) {
+	var s;
 
-		for ( j = 0; j < slices; j ++ ) {
+	for ( s = 1; s <= steps; s ++ ) {
 
-			a = i * sliceCount + j;
-			b = i * sliceCount + j + 1;
-			c = (i + 1) * sliceCount + j + 1;
-			d = (i + 1) * sliceCount + j;
+		for ( i = 0; i < vlen; i ++ ) {
 
-			uva = new THREE.Vector2( j / slices, i / stacks );
-			uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks );
-			uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks );
-			uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks );
+			vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
 
-			faces.push( new THREE.Face3( a, b, d ) );
-			uvs.push( [ uva, uvb, uvd ] );
+			if ( ! extrudeByPath ) {
 
-			faces.push( new THREE.Face3( b, c, d ) );
-			uvs.push( [ uvb.clone(), uvc, uvd.clone() ] );
+				v( vert.x, vert.y, amount / steps * s );
 
-		}
+			} else {
 
-	}
+				// v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
 
-	// console.log(this);
+				normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x );
+				binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y );
 
-	// magic bullet
-	// var diff = this.mergeVertices();
-	// console.log('removed ', diff, ' vertices by merging');
+				position2.copy( extrudePts[s] ).add( normal ).add( binormal );
 
-	this.computeFaceNormals();
-	this.computeVertexNormals();
+				v( position2.x, position2.y, position2.z );
 
-};
+			}
 
-THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype );
+		}
 
-// File:src/extras/helpers/AxisHelper.js
+	}
 
-/**
- * @author sroucheray / http://sroucheray.org/
- * @author mrdoob / http://mrdoob.com/
- */
 
-THREE.AxisHelper = function ( size ) {
+	// Add bevel segments planes
 
-	size = size || 1;
+	//for ( b = 1; b <= bevelSegments; b ++ ) {
+	for ( b = bevelSegments - 1; b >= 0; b -- ) {
 
-	var vertices = new Float32Array( [
-		0, 0, 0,  size, 0, 0,
-		0, 0, 0,  0, size, 0,
-		0, 0, 0,  0, 0, size
-	] );
+		t = b / bevelSegments;
+		z = bevelThickness * ( 1 - t );
+		//bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) );
+		bs = bevelSize * Math.sin ( t * Math.PI/2 ) ;
 
-	var colors = new Float32Array( [
-		1, 0, 0,  1, 0.6, 0,
-		0, 1, 0,  0.6, 1, 0,
-		0, 0, 1,  0, 0.6, 1
-	] );
+		// contract shape
 
-	var geometry = new THREE.BufferGeometry();
-	geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
-	geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
+		for ( i = 0, il = contour.length; i < il; i ++ ) {
 
-	var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
+			vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
+			v( vert.x, vert.y,  amount + z );
 
-	THREE.Line.call( this, geometry, material, THREE.LinePieces );
+		}
 
-};
+		// expand holes
 
-THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype );
+		for ( h = 0, hl = holes.length; h < hl; h ++ ) {
 
-// File:src/extras/helpers/ArrowHelper.js
+			ahole = holes[ h ];
+			oneHoleMovements = holesMovements[ h ];
 
-/**
- * @author WestLangley / http://github.com/WestLangley
- * @author zz85 / http://github.com/zz85
- * @author bhouston / http://exocortex.com
- *
- * Creates an arrow for visualizing directions
- *
- * Parameters:
- *  dir - Vector3
- *  origin - Vector3
- *  length - Number
- *  color - color in hex value
- *  headLength - Number
- *  headWidth - Number
- */
+			for ( i = 0, il = ahole.length; i < il; i ++ ) {
 
-THREE.ArrowHelper = ( function () {
+				vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
 
-	var lineGeometry = new THREE.Geometry();
-	lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) );
+				if ( ! extrudeByPath ) {
 
-	var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 );
-	coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) );
+					v( vert.x, vert.y,  amount + z );
 
-	return function ( dir, origin, length, color, headLength, headWidth ) {
+				} else {
 
-		// dir is assumed to be normalized
+					v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
 
-		THREE.Object3D.call( this );
+				}
 
-		if ( color === undefined ) color = 0xffff00;
-		if ( length === undefined ) length = 1;
-		if ( headLength === undefined ) headLength = 0.2 * length;
-		if ( headWidth === undefined ) headWidth = 0.2 * headLength;
+			}
 
-		this.position.copy( origin );
+		}
 
-		this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) );
-		this.line.matrixAutoUpdate = false;
-		this.add( this.line );
+	}
 
-		this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) );
-		this.cone.matrixAutoUpdate = false;
-		this.add( this.cone );
+	/* Faces */
 
-		this.setDirection( dir );
-		this.setLength( length, headLength, headWidth );
+	// Top and bottom faces
 
-	}
+	buildLidFaces();
 
-}() );
+	// Sides faces
 
-THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype );
+	buildSideFaces();
 
-THREE.ArrowHelper.prototype.setDirection = ( function () {
 
-	var axis = new THREE.Vector3();
-	var radians;
+	/////  Internal functions
 
-	return function ( dir ) {
+	function buildLidFaces() {
 
-		// dir is assumed to be normalized
+		if ( bevelEnabled ) {
 
-		if ( dir.y > 0.99999 ) {
+			var layer = 0 ; // steps + 1
+			var offset = vlen * layer;
 
-			this.quaternion.set( 0, 0, 0, 1 );
+			// Bottom faces
 
-		} else if ( dir.y < - 0.99999 ) {
+			for ( i = 0; i < flen; i ++ ) {
 
-			this.quaternion.set( 1, 0, 0, 0 );
+				face = faces[ i ];
+				f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset );
 
-		} else {
+			}
 
-			axis.set( dir.z, 0, - dir.x ).normalize();
+			layer = steps + bevelSegments * 2;
+			offset = vlen * layer;
 
-			radians = Math.acos( dir.y );
+			// Top faces
 
-			this.quaternion.setFromAxisAngle( axis, radians );
+			for ( i = 0; i < flen; i ++ ) {
 
-		}
+				face = faces[ i ];
+				f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );
 
-	};
+			}
 
-}() );
+		} else {
 
-THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) {
+			// Bottom faces
 
-	if ( headLength === undefined ) headLength = 0.2 * length;
-	if ( headWidth === undefined ) headWidth = 0.2 * headLength;
+			for ( i = 0; i < flen; i ++ ) {
 
-	this.line.scale.set( 1, length, 1 );
-	this.line.updateMatrix();
+				face = faces[ i ];
+				f3( face[ 2 ], face[ 1 ], face[ 0 ] );
 
-	this.cone.scale.set( headWidth, headLength, headWidth );
-	this.cone.position.y = length;
-	this.cone.updateMatrix();
+			}
 
-};
+			// Top faces
 
-THREE.ArrowHelper.prototype.setColor = function ( color ) {
+			for ( i = 0; i < flen; i ++ ) {
 
-	this.line.material.color.set( color );
-	this.cone.material.color.set( color );
+				face = faces[ i ];
+				f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );
 
-};
+			}
+		}
 
-// File:src/extras/helpers/BoxHelper.js
+	}
 
-/**
- * @author mrdoob / http://mrdoob.com/
- */
+	// Create faces for the z-sides of the shape
 
-THREE.BoxHelper = function ( object ) {
+	function buildSideFaces() {
 
-	var geometry = new THREE.BufferGeometry();
-	geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( 72 ), 3 ) );
+		var layeroffset = 0;
+		sidewalls( contour, layeroffset );
+		layeroffset += contour.length;
 
-	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ), THREE.LinePieces );
+		for ( h = 0, hl = holes.length;  h < hl; h ++ ) {
 
-	if ( object !== undefined ) {
+			ahole = holes[ h ];
+			sidewalls( ahole, layeroffset );
 
-		this.update( object );
+			//, true
+			layeroffset += ahole.length;
+
+		}
 
 	}
 
-};
+	function sidewalls( contour, layeroffset ) {
 
-THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype );
+		var j, k;
+		i = contour.length;
 
-THREE.BoxHelper.prototype.update = function ( object ) {
+		while ( --i >= 0 ) {
 
-	var geometry = object.geometry;
+			j = i;
+			k = i - 1;
+			if ( k < 0 ) k = contour.length - 1;
 
-	if ( geometry.boundingBox === null ) {
+			//console.log('b', i,j, i-1, k,vertices.length);
 
-		geometry.computeBoundingBox();
+			var s = 0, sl = steps  + bevelSegments * 2;
 
-	}
+			for ( s = 0; s < sl; s ++ ) {
 
-	var min = geometry.boundingBox.min;
-	var max = geometry.boundingBox.max;
+				var slen1 = vlen * s;
+				var slen2 = vlen * ( s + 1 );
 
-	/*
-	  5____4
-	1/___0/|
-	| 6__|_7
-	2/___3/
+				var a = layeroffset + j + slen1,
+					b = layeroffset + k + slen1,
+					c = layeroffset + k + slen2,
+					d = layeroffset + j + slen2;
 
-	0: max.x, max.y, max.z
-	1: min.x, max.y, max.z
-	2: min.x, min.y, max.z
-	3: max.x, min.y, max.z
-	4: max.x, max.y, min.z
-	5: min.x, max.y, min.z
-	6: min.x, min.y, min.z
-	7: max.x, min.y, min.z
-	*/
+				f4( a, b, c, d, contour, s, sl, j, k );
 
-	var vertices = this.geometry.attributes.position.array;
+			}
+		}
 
-	vertices[  0 ] = max.x; vertices[  1 ] = max.y; vertices[  2 ] = max.z;
-	vertices[  3 ] = min.x; vertices[  4 ] = max.y; vertices[  5 ] = max.z;
+	}
 
-	vertices[  6 ] = min.x; vertices[  7 ] = max.y; vertices[  8 ] = max.z;
-	vertices[  9 ] = min.x; vertices[ 10 ] = min.y; vertices[ 11 ] = max.z;
 
-	vertices[ 12 ] = min.x; vertices[ 13 ] = min.y; vertices[ 14 ] = max.z;
-	vertices[ 15 ] = max.x; vertices[ 16 ] = min.y; vertices[ 17 ] = max.z;
+	function v( x, y, z ) {
 
-	vertices[ 18 ] = max.x; vertices[ 19 ] = min.y; vertices[ 20 ] = max.z;
-	vertices[ 21 ] = max.x; vertices[ 22 ] = max.y; vertices[ 23 ] = max.z;
+		scope.vertices.push( new THREE.Vector3( x, y, z ) );
 
-	//
+	}
 
-	vertices[ 24 ] = max.x; vertices[ 25 ] = max.y; vertices[ 26 ] = min.z;
-	vertices[ 27 ] = min.x; vertices[ 28 ] = max.y; vertices[ 29 ] = min.z;
+	function f3( a, b, c ) {
 
-	vertices[ 30 ] = min.x; vertices[ 31 ] = max.y; vertices[ 32 ] = min.z;
-	vertices[ 33 ] = min.x; vertices[ 34 ] = min.y; vertices[ 35 ] = min.z;
+		a += shapesOffset;
+		b += shapesOffset;
+		c += shapesOffset;
 
-	vertices[ 36 ] = min.x; vertices[ 37 ] = min.y; vertices[ 38 ] = min.z;
-	vertices[ 39 ] = max.x; vertices[ 40 ] = min.y; vertices[ 41 ] = min.z;
+		// normal, color, material
+		scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
 
-	vertices[ 42 ] = max.x; vertices[ 43 ] = min.y; vertices[ 44 ] = min.z;
-	vertices[ 45 ] = max.x; vertices[ 46 ] = max.y; vertices[ 47 ] = min.z;
+		var uvs = uvgen.generateTopUV( scope, a, b, c );
 
-	//
+ 		scope.faceVertexUvs[ 0 ].push( uvs );
 
-	vertices[ 48 ] = max.x; vertices[ 49 ] = max.y; vertices[ 50 ] = max.z;
-	vertices[ 51 ] = max.x; vertices[ 52 ] = max.y; vertices[ 53 ] = min.z;
+	}
 
-	vertices[ 54 ] = min.x; vertices[ 55 ] = max.y; vertices[ 56 ] = max.z;
-	vertices[ 57 ] = min.x; vertices[ 58 ] = max.y; vertices[ 59 ] = min.z;
+	function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) {
 
-	vertices[ 60 ] = min.x; vertices[ 61 ] = min.y; vertices[ 62 ] = max.z;
-	vertices[ 63 ] = min.x; vertices[ 64 ] = min.y; vertices[ 65 ] = min.z;
+		a += shapesOffset;
+		b += shapesOffset;
+		c += shapesOffset;
+		d += shapesOffset;
 
-	vertices[ 66 ] = max.x; vertices[ 67 ] = min.y; vertices[ 68 ] = max.z;
-	vertices[ 69 ] = max.x; vertices[ 70 ] = min.y; vertices[ 71 ] = min.z;
+ 		scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) );
+ 		scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) );
 
-	this.geometry.attributes.position.needsUpdate = true;
+ 		var uvs = uvgen.generateSideWallUV( scope, a, b, c, d );
 
-	this.geometry.computeBoundingSphere();
+ 		scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] );
+ 		scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] );
 
-	this.matrix = object.matrixWorld;
-	this.matrixAutoUpdate = false;
+	}
 
 };
 
-// File:src/extras/helpers/BoundingBoxHelper.js
+THREE.ExtrudeGeometry.WorldUVGenerator = {
 
-/**
- * @author WestLangley / http://github.com/WestLangley
- */
+	generateTopUV: function ( geometry, indexA, indexB, indexC ) {
 
-// a helper to show the world-axis-aligned bounding box for an object
+		var vertices = geometry.vertices;
 
-THREE.BoundingBoxHelper = function ( object, hex ) {
+		var a = vertices[ indexA ];
+		var b = vertices[ indexB ];
+		var c = vertices[ indexC ];
 
-	var color = ( hex !== undefined ) ? hex : 0x888888;
+		return [
+			new THREE.Vector2( a.x, a.y ),
+			new THREE.Vector2( b.x, b.y ),
+			new THREE.Vector2( c.x, c.y )
+		];
 
-	this.object = object;
+	},
 
-	this.box = new THREE.Box3();
+	generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) {
 
-	THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) );
+		var vertices = geometry.vertices;
+
+		var a = vertices[ indexA ];
+		var b = vertices[ indexB ];
+		var c = vertices[ indexC ];
+		var d = vertices[ indexD ];
 
+		if ( Math.abs( a.y - b.y ) < 0.01 ) {
+			return [
+				new THREE.Vector2( a.x, 1 - a.z ),
+				new THREE.Vector2( b.x, 1 - b.z ),
+				new THREE.Vector2( c.x, 1 - c.z ),
+				new THREE.Vector2( d.x, 1 - d.z )
+			];
+		} else {
+			return [
+				new THREE.Vector2( a.y, 1 - a.z ),
+				new THREE.Vector2( b.y, 1 - b.z ),
+				new THREE.Vector2( c.y, 1 - c.z ),
+				new THREE.Vector2( d.y, 1 - d.z )
+			];
+		}
+	}
 };
 
-THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype );
+// File:src/extras/geometries/ShapeGeometry.js
 
-THREE.BoundingBoxHelper.prototype.update = function () {
+/**
+ * @author jonobr1 / http://jonobr1.com
+ *
+ * Creates a one-sided polygonal geometry from a path shape. Similar to
+ * ExtrudeGeometry.
+ *
+ * parameters = {
+ *
+ *	curveSegments: <int>, // number of points on the curves. NOT USED AT THE MOMENT.
+ *
+ *	material: <int> // material index for front and back faces
+ *	uvGenerator: <Object> // object that provides UV generator functions
+ *
+ * }
+ **/
 
-	this.box.setFromObject( this.object );
+THREE.ShapeGeometry = function ( shapes, options ) {
 
-	this.box.size( this.scale );
+	THREE.Geometry.call( this );
 
-	this.box.center( this.position );
+	this.type = 'ShapeGeometry';
+
+	if ( shapes instanceof Array === false ) shapes = [ shapes ];
+
+	this.addShapeList( shapes, options );
+
+	this.computeFaceNormals();
 
 };
 
-// File:src/extras/helpers/CameraHelper.js
+THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
 /**
- * @author alteredq / http://alteredqualia.com/
- *
- *	- shows frustum, line of sight and up of the camera
- *	- suitable for fast updates
- * 	- based on frustum visualization in lightgl.js shadowmap example
- *		http://evanw.github.com/lightgl.js/tests/shadowmap.html
+ * Add an array of shapes to THREE.ShapeGeometry.
  */
+THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) {
 
-THREE.CameraHelper = function ( camera ) {
+	for ( var i = 0, l = shapes.length; i < l; i ++ ) {
 
-	var geometry = new THREE.Geometry();
-	var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } );
+		this.addShape( shapes[ i ], options );
 
-	var pointMap = {};
+	}
 
-	// colors
+	return this;
 
-	var hexFrustum = 0xffaa00;
-	var hexCone = 0xff0000;
-	var hexUp = 0x00aaff;
-	var hexTarget = 0xffffff;
-	var hexCross = 0x333333;
+};
 
-	// near
+/**
+ * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry.
+ */
+THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) {
 
-	addLine( "n1", "n2", hexFrustum );
-	addLine( "n2", "n4", hexFrustum );
-	addLine( "n4", "n3", hexFrustum );
-	addLine( "n3", "n1", hexFrustum );
+	if ( options === undefined ) options = {};
+	var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
 
-	// far
+	var material = options.material;
+	var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator;
 
-	addLine( "f1", "f2", hexFrustum );
-	addLine( "f2", "f4", hexFrustum );
-	addLine( "f4", "f3", hexFrustum );
-	addLine( "f3", "f1", hexFrustum );
+	//
 
-	// sides
+	var i, l, hole, s;
 
-	addLine( "n1", "f1", hexFrustum );
-	addLine( "n2", "f2", hexFrustum );
-	addLine( "n3", "f3", hexFrustum );
-	addLine( "n4", "f4", hexFrustum );
+	var shapesOffset = this.vertices.length;
+	var shapePoints = shape.extractPoints( curveSegments );
 
-	// cone
+	var vertices = shapePoints.shape;
+	var holes = shapePoints.holes;
 
-	addLine( "p", "n1", hexCone );
-	addLine( "p", "n2", hexCone );
-	addLine( "p", "n3", hexCone );
-	addLine( "p", "n4", hexCone );
+	var reverse = ! THREE.Shape.Utils.isClockWise( vertices );
 
-	// up
+	if ( reverse ) {
 
-	addLine( "u1", "u2", hexUp );
-	addLine( "u2", "u3", hexUp );
-	addLine( "u3", "u1", hexUp );
+		vertices = vertices.reverse();
 
-	// target
+		// Maybe we should also check if holes are in the opposite direction, just to be safe...
 
-	addLine( "c", "t", hexTarget );
-	addLine( "p", "c", hexCross );
+		for ( i = 0, l = holes.length; i < l; i ++ ) {
 
-	// cross
+			hole = holes[ i ];
 
-	addLine( "cn1", "cn2", hexCross );
-	addLine( "cn3", "cn4", hexCross );
+			if ( THREE.Shape.Utils.isClockWise( hole ) ) {
 
-	addLine( "cf1", "cf2", hexCross );
-	addLine( "cf3", "cf4", hexCross );
+				holes[ i ] = hole.reverse();
 
-	function addLine( a, b, hex ) {
+			}
 
-		addPoint( a, hex );
-		addPoint( b, hex );
+		}
 
-	}
+		reverse = false;
 
-	function addPoint( id, hex ) {
+	}
 
-		geometry.vertices.push( new THREE.Vector3() );
-		geometry.colors.push( new THREE.Color( hex ) );
+	var faces = THREE.Shape.Utils.triangulateShape( vertices, holes );
 
-		if ( pointMap[ id ] === undefined ) {
+	// Vertices
 
-			pointMap[ id ] = [];
+	var contour = vertices;
 
-		}
+	for ( i = 0, l = holes.length; i < l; i ++ ) {
 
-		pointMap[ id ].push( geometry.vertices.length - 1 );
+		hole = holes[ i ];
+		vertices = vertices.concat( hole );
 
 	}
 
-	THREE.Line.call( this, geometry, material, THREE.LinePieces );
-
-	this.camera = camera;
-	this.matrix = camera.matrixWorld;
-	this.matrixAutoUpdate = false;
-
-	this.pointMap = pointMap;
+	//
 
-	this.update();
+	var vert, vlen = vertices.length;
+	var face, flen = faces.length;
+	var cont, clen = contour.length;
 
-};
+	for ( i = 0; i < vlen; i ++ ) {
 
-THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype );
+		vert = vertices[ i ];
 
-THREE.CameraHelper.prototype.update = function () {
+		this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) );
 
-	var geometry, pointMap;
-	
-	var vector = new THREE.Vector3();
-	var camera = new THREE.Camera();
+	}
 
-	var setPoint = function ( point, x, y, z ) {
+	for ( i = 0; i < flen; i ++ ) {
 
-		vector.set( x, y, z ).unproject( camera );
+		face = faces[ i ];
 
-		var points = pointMap[ point ];
+		var a = face[ 0 ] + shapesOffset;
+		var b = face[ 1 ] + shapesOffset;
+		var c = face[ 2 ] + shapesOffset;
 
-		if ( points !== undefined ) {
+		this.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
+		this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) );
 
-			for ( var i = 0, il = points.length; i < il; i ++ ) {
+	}
 
-				geometry.vertices[ points[ i ] ].copy( vector );
+};
 
-			}
+// File:src/extras/geometries/LatheGeometry.js
 
-		}
+/**
+ * @author astrodud / http://astrodud.isgreat.org/
+ * @author zz85 / https://github.com/zz85
+ * @author bhouston / http://exocortex.com
+ */
 
-	};
+// points - to create a closed torus, one must use a set of points 
+//    like so: [ a, b, c, d, a ], see first is the same as last.
+// segments - the number of circumference segments to create
+// phiStart - the starting radian
+// phiLength - the radian (0 to 2*PI) range of the lathed section
+//    2*pi is a closed lathe, less than 2PI is a portion.
 
-	return function () {
+THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) {
 
-		geometry = this.geometry;
-		pointMap = this.pointMap;
+	THREE.Geometry.call( this );
 
-		var w = 1, h = 1;
+	this.type = 'LatheGeometry';
 
-		// we need just camera projection matrix
-		// world matrix must be identity
+	this.parameters = {
+		points: points,
+		segments: segments,
+		phiStart: phiStart,
+		phiLength: phiLength
+	};
 
-		camera.projectionMatrix.copy( this.camera.projectionMatrix );
+	segments = segments || 12;
+	phiStart = phiStart || 0;
+	phiLength = phiLength || 2 * Math.PI;
 
-		// center / target
+	var inversePointLength = 1.0 / ( points.length - 1 );
+	var inverseSegments = 1.0 / segments;
 
-		setPoint( "c", 0, 0, - 1 );
-		setPoint( "t", 0, 0,  1 );
+	for ( var i = 0, il = segments; i <= il; i ++ ) {
 
-		// near
+		var phi = phiStart + i * inverseSegments * phiLength;
 
-		setPoint( "n1", - w, - h, - 1 );
-		setPoint( "n2",   w, - h, - 1 );
-		setPoint( "n3", - w,   h, - 1 );
-		setPoint( "n4",   w,   h, - 1 );
+		var c = Math.cos( phi ),
+			s = Math.sin( phi );
 
-		// far
+		for ( var j = 0, jl = points.length; j < jl; j ++ ) {
 
-		setPoint( "f1", - w, - h, 1 );
-		setPoint( "f2",   w, - h, 1 );
-		setPoint( "f3", - w,   h, 1 );
-		setPoint( "f4",   w,   h, 1 );
+			var pt = points[ j ];
 
-		// up
+			var vertex = new THREE.Vector3();
 
-		setPoint( "u1",   w * 0.7, h * 1.1, - 1 );
-		setPoint( "u2", - w * 0.7, h * 1.1, - 1 );
-		setPoint( "u3",         0, h * 2,   - 1 );
+			vertex.x = c * pt.x - s * pt.y;
+			vertex.y = s * pt.x + c * pt.y;
+			vertex.z = pt.z;
 
-		// cross
+			this.vertices.push( vertex );
 
-		setPoint( "cf1", - w,   0, 1 );
-		setPoint( "cf2",   w,   0, 1 );
-		setPoint( "cf3",   0, - h, 1 );
-		setPoint( "cf4",   0,   h, 1 );
+		}
 
-		setPoint( "cn1", - w,   0, - 1 );
-		setPoint( "cn2",   w,   0, - 1 );
-		setPoint( "cn3",   0, - h, - 1 );
-		setPoint( "cn4",   0,   h, - 1 );
+	}
 
-		geometry.verticesNeedUpdate = true;
+	var np = points.length;
 
-	};
+	for ( var i = 0, il = segments; i < il; i ++ ) {
 
-}();
+		for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) {
 
-// File:src/extras/helpers/DirectionalLightHelper.js
+			var base = j + np * i;
+			var a = base;
+			var b = base + np;
+			var c = base + 1 + np;
+			var d = base + 1;
 
-/**
- * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com/
- * @author WestLangley / http://github.com/WestLangley
- */
+			var u0 = i * inverseSegments;
+			var v0 = j * inversePointLength;
+			var u1 = u0 + inverseSegments;
+			var v1 = v0 + inversePointLength;
 
-THREE.DirectionalLightHelper = function ( light, size ) {
+			this.faces.push( new THREE.Face3( a, b, d ) );
 
-	THREE.Object3D.call( this );
+			this.faceVertexUvs[ 0 ].push( [
 
-	this.light = light;
-	this.light.updateMatrixWorld();
+				new THREE.Vector2( u0, v0 ),
+				new THREE.Vector2( u1, v0 ),
+				new THREE.Vector2( u0, v1 )
 
-	this.matrix = light.matrixWorld;
-	this.matrixAutoUpdate = false;
+			] );
 
-	size = size || 1;
+			this.faces.push( new THREE.Face3( b, c, d ) );
 
-	var geometry = new THREE.Geometry();
-	geometry.vertices.push(
-		new THREE.Vector3( - size,   size, 0 ),
-		new THREE.Vector3(   size,   size, 0 ),
-		new THREE.Vector3(   size, - size, 0 ),
-		new THREE.Vector3( - size, - size, 0 ),
-		new THREE.Vector3( - size,   size, 0 )
-	);
+			this.faceVertexUvs[ 0 ].push( [
 
-	var material = new THREE.LineBasicMaterial( { fog: false } );
-	material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+				new THREE.Vector2( u1, v0 ),
+				new THREE.Vector2( u1, v1 ),
+				new THREE.Vector2( u0, v1 )
 
-	this.lightPlane = new THREE.Line( geometry, material );
-	this.add( this.lightPlane );
+			] );
 
-	geometry = new THREE.Geometry();
-	geometry.vertices.push(
-		new THREE.Vector3(),
-		new THREE.Vector3()
-	);
 
-	material = new THREE.LineBasicMaterial( { fog: false } );
-	material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+		}
 
-	this.targetLine = new THREE.Line( geometry, material );
-	this.add( this.targetLine );
+	}
 
-	this.update();
+	this.mergeVertices();
+	this.computeFaceNormals();
+	this.computeVertexNormals();
 
 };
 
-THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-THREE.DirectionalLightHelper.prototype.dispose = function () {
+// File:src/extras/geometries/PlaneGeometry.js
 
-	this.lightPlane.geometry.dispose();
-	this.lightPlane.material.dispose();
-	this.targetLine.geometry.dispose();
-	this.targetLine.material.dispose();
-};
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
+ */
 
-THREE.DirectionalLightHelper.prototype.update = function () {
+THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) {
 
-	var v1 = new THREE.Vector3();
-	var v2 = new THREE.Vector3();
-	var v3 = new THREE.Vector3();
+	console.info( 'THREE.PlaneGeometry: Consider using THREE.PlaneBufferGeometry for lower memory footprint.' );
 
-	return function () {
+	THREE.Geometry.call( this );
 
-		v1.setFromMatrixPosition( this.light.matrixWorld );
-		v2.setFromMatrixPosition( this.light.target.matrixWorld );
-		v3.subVectors( v2, v1 );
+	this.type = 'PlaneGeometry';
 
-		this.lightPlane.lookAt( v3 );
-		this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+	this.parameters = {
+		width: width,
+		height: height,
+		widthSegments: widthSegments,
+		heightSegments: heightSegments
+	};
 
-		this.targetLine.geometry.vertices[ 1 ].copy( v3 );
-		this.targetLine.geometry.verticesNeedUpdate = true;
-		this.targetLine.material.color.copy( this.lightPlane.material.color );
+	this.fromBufferGeometry( new THREE.PlaneBufferGeometry( width, height, widthSegments, heightSegments ) );
 
-	};
+};
 
-}();
+THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-// File:src/extras/helpers/EdgesHelper.js
+// File:src/extras/geometries/PlaneBufferGeometry.js
 
 /**
- * @author WestLangley / http://github.com/WestLangley
+ * @author mrdoob / http://mrdoob.com/
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
  */
 
-THREE.EdgesHelper = function ( object, hex ) {
+THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) {
 
-	var color = ( hex !== undefined ) ? hex : 0xffffff;
+	THREE.BufferGeometry.call( this );
 
-	var edge = [ 0, 0 ], hash = {};
-	var sortFunction = function ( a, b ) { return a - b };
+	this.type = 'PlaneBufferGeometry';
 
-	var keys = [ 'a', 'b', 'c' ];
-	var geometry = new THREE.BufferGeometry();
+	this.parameters = {
+		width: width,
+		height: height,
+		widthSegments: widthSegments,
+		heightSegments: heightSegments
+	};
 
-	var geometry2 = object.geometry.clone();
+	var width_half = width / 2;
+	var height_half = height / 2;
 
-	geometry2.mergeVertices();
-	geometry2.computeFaceNormals();
+	var gridX = widthSegments || 1;
+	var gridY = heightSegments || 1;
 
-	var vertices = geometry2.vertices;
-	var faces = geometry2.faces;
-	var numEdges = 0;
+	var gridX1 = gridX + 1;
+	var gridY1 = gridY + 1;
 
-	for ( var i = 0, l = faces.length; i < l; i ++ ) {
+	var segment_width = width / gridX;
+	var segment_height = height / gridY;
 
-		var face = faces[ i ];
+	var vertices = new Float32Array( gridX1 * gridY1 * 3 );
+	var normals = new Float32Array( gridX1 * gridY1 * 3 );
+	var uvs = new Float32Array( gridX1 * gridY1 * 2 );
 
-		for ( var j = 0; j < 3; j ++ ) {
+	var offset = 0;
+	var offset2 = 0;
 
-			edge[ 0 ] = face[ keys[ j ] ];
-			edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
-			edge.sort( sortFunction );
+	for ( var iy = 0; iy < gridY1; iy ++ ) {
+
+		var y = iy * segment_height - height_half;
 
-			var key = edge.toString();
+		for ( var ix = 0; ix < gridX1; ix ++ ) {
 
-			if ( hash[ key ] === undefined ) {
+			var x = ix * segment_width - width_half;
 
-				hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined };
-				numEdges ++;
+			vertices[ offset     ] = x;
+			vertices[ offset + 1 ] = - y;
 
-			} else {
+			normals[ offset + 2 ] = 1;
 
-				hash[ key ].face2 = i;
+			uvs[ offset2     ] = ix / gridX;
+			uvs[ offset2 + 1 ] = 1 - ( iy / gridY );
 
-			}
+			offset += 3;
+			offset2 += 2;
 
 		}
 
 	}
 
-	geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( numEdges * 2 * 3 ), 3 ) );
+	offset = 0;
 
-	var coords = geometry.attributes.position.array;
+	var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 );
 
-	var index = 0;
+	for ( var iy = 0; iy < gridY; iy ++ ) {
 
-	for ( var key in hash ) {
+		for ( var ix = 0; ix < gridX; ix ++ ) {
 
-		var h = hash[ key ];
+			var a = ix + gridX1 * iy;
+			var b = ix + gridX1 * ( iy + 1 );
+			var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
+			var d = ( ix + 1 ) + gridX1 * iy;
 
-		if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) < 0.9999 ) { // hardwired const OK
+			indices[ offset     ] = a;
+			indices[ offset + 1 ] = b;
+			indices[ offset + 2 ] = d;
 
-			var vertex = vertices[ h.vert1 ];
-			coords[ index ++ ] = vertex.x;
-			coords[ index ++ ] = vertex.y;
-			coords[ index ++ ] = vertex.z;
+			indices[ offset + 3 ] = b;
+			indices[ offset + 4 ] = c;
+			indices[ offset + 5 ] = d;
 
-			vertex = vertices[ h.vert2 ];
-			coords[ index ++ ] = vertex.x;
-			coords[ index ++ ] = vertex.y;
-			coords[ index ++ ] = vertex.z;
+			offset += 6;
 
 		}
 
 	}
 
-	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces );
-
-	this.matrix = object.matrixWorld;
-	this.matrixAutoUpdate = false;
+	this.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) );
+	this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+	this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
+	this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
 
 };
 
-THREE.EdgesHelper.prototype = Object.create( THREE.Line.prototype );
+THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
 
-// File:src/extras/helpers/FaceNormalsHelper.js
+// File:src/extras/geometries/RingGeometry.js
 
 /**
- * @author mrdoob / http://mrdoob.com/
- * @author WestLangley / http://github.com/WestLangley
-*/
-
-THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) {
-
-	this.object = object;
+ * @author Kaleb Murphy
+ */
 
-	this.size = ( size !== undefined ) ? size : 1;
+THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {
 
-	var color = ( hex !== undefined ) ? hex : 0xffff00;
+	THREE.Geometry.call( this );
 
-	var width = ( linewidth !== undefined ) ? linewidth : 1;
+	this.type = 'RingGeometry';
 
-	var geometry = new THREE.Geometry();
+	this.parameters = {
+		innerRadius: innerRadius,
+		outerRadius: outerRadius,
+		thetaSegments: thetaSegments,
+		phiSegments: phiSegments,
+		thetaStart: thetaStart,
+		thetaLength: thetaLength
+	};
 
-	var faces = this.object.geometry.faces;
+	innerRadius = innerRadius || 0;
+	outerRadius = outerRadius || 50;
 
-	for ( var i = 0, l = faces.length; i < l; i ++ ) {
+	thetaStart = thetaStart !== undefined ? thetaStart : 0;
+	thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
 
-		geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() );
+	thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;
+	phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8;
 
-	}
+	var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );
 
-	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
+	for ( i = 0; i < phiSegments + 1; i ++ ) { // concentric circles inside ring
 
-	this.matrixAutoUpdate = false;
+		for ( o = 0; o < thetaSegments + 1; o ++ ) { // number of segments per circle
 
-	this.normalMatrix = new THREE.Matrix3();
+			var vertex = new THREE.Vector3();
+			var segment = thetaStart + o / thetaSegments * thetaLength;
+			vertex.x = radius * Math.cos( segment );
+			vertex.y = radius * Math.sin( segment );
 
-	this.update();
+			this.vertices.push( vertex );
+			uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) );
+		}
 
-};
+		radius += radiusStep;
 
-THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype );
+	}
 
-THREE.FaceNormalsHelper.prototype.update = function () {
+	var n = new THREE.Vector3( 0, 0, 1 );
 
-	var vertices = this.geometry.vertices;
+	for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring
 
-	var object = this.object;
-	var objectVertices = object.geometry.vertices;
-	var objectFaces = object.geometry.faces;
-	var objectWorldMatrix = object.matrixWorld;
+		var thetaSegment = i * (thetaSegments + 1);
 
-	object.updateMatrixWorld( true );
+		for ( o = 0; o < thetaSegments ; o ++ ) { // number of segments per circle
 
-	this.normalMatrix.getNormalMatrix( objectWorldMatrix );
+			var segment = o + thetaSegment;
 
-	for ( var i = 0, i2 = 0, l = objectFaces.length; i < l; i ++, i2 += 2 ) {
+			var v1 = segment;
+			var v2 = segment + thetaSegments + 1;
+			var v3 = segment + thetaSegments + 2;
 
-		var face = objectFaces[ i ];
+			this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]);
 
-		vertices[ i2 ].copy( objectVertices[ face.a ] )
-			.add( objectVertices[ face.b ] )
-			.add( objectVertices[ face.c ] )
-			.divideScalar( 3 )
-			.applyMatrix4( objectWorldMatrix );
+			v1 = segment;
+			v2 = segment + thetaSegments + 2;
+			v3 = segment + 1;
 
-		vertices[ i2 + 1 ].copy( face.normal )
-			.applyMatrix3( this.normalMatrix )
-			.normalize()
-			.multiplyScalar( this.size )
-			.add( vertices[ i2 ] );
+			this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]);
 
+		}
 	}
 
-	this.geometry.verticesNeedUpdate = true;
+	this.computeFaceNormals();
 
-	return this;
+	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
 
 };
 
+THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
 
-// File:src/extras/helpers/GridHelper.js
+// File:src/extras/geometries/SphereGeometry.js
 
 /**
  * @author mrdoob / http://mrdoob.com/
  */
 
-THREE.GridHelper = function ( size, step ) {
-
-	var geometry = new THREE.Geometry();
-	var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
+THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
 
-	this.color1 = new THREE.Color( 0x444444 );
-	this.color2 = new THREE.Color( 0x888888 );
+	THREE.Geometry.call( this );
 
-	for ( var i = - size; i <= size; i += step ) {
+	this.type = 'SphereGeometry';
 
-		geometry.vertices.push(
-			new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ),
-			new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size )
-		);
+	this.parameters = {
+		radius: radius,
+		widthSegments: widthSegments,
+		heightSegments: heightSegments,
+		phiStart: phiStart,
+		phiLength: phiLength,
+		thetaStart: thetaStart,
+		thetaLength: thetaLength 
+	};
 
-		var color = i === 0 ? this.color1 : this.color2;
+	radius = radius || 50;
 
-		geometry.colors.push( color, color, color, color );
+	widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );
+	heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 );
 
-	}
+	phiStart = phiStart !== undefined ? phiStart : 0;
+	phiLength = phiLength !== undefined ? phiLength : Math.PI * 2;
 
-	THREE.Line.call( this, geometry, material, THREE.LinePieces );
+	thetaStart = thetaStart !== undefined ? thetaStart : 0;
+	thetaLength = thetaLength !== undefined ? thetaLength : Math.PI;
 
-};
+	var x, y, vertices = [], uvs = [];
 
-THREE.GridHelper.prototype = Object.create( THREE.Line.prototype );
+	for ( y = 0; y <= heightSegments; y ++ ) {
 
-THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) {
+		var verticesRow = [];
+		var uvsRow = [];
 
-	this.color1.set( colorCenterLine );
-	this.color2.set( colorGrid );
+		for ( x = 0; x <= widthSegments; x ++ ) {
 
-	this.geometry.colorsNeedUpdate = true;
+			var u = x / widthSegments;
+			var v = y / heightSegments;
 
-}
+			var vertex = new THREE.Vector3();
+			vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
+			vertex.y = radius * Math.cos( thetaStart + v * thetaLength );
+			vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
 
-// File:src/extras/helpers/HemisphereLightHelper.js
+			this.vertices.push( vertex );
 
-/**
- * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com/
- */
+			verticesRow.push( this.vertices.length - 1 );
+			uvsRow.push( new THREE.Vector2( u, 1 - v ) );
 
-THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) {
+		}
 
-	THREE.Object3D.call( this );
+		vertices.push( verticesRow );
+		uvs.push( uvsRow );
 
-	this.light = light;
-	this.light.updateMatrixWorld();
+	}
 
-	this.matrix = light.matrixWorld;
-	this.matrixAutoUpdate = false;
+	for ( y = 0; y < heightSegments; y ++ ) {
 
-	this.colors = [ new THREE.Color(), new THREE.Color() ];
+		for ( x = 0; x < widthSegments; x ++ ) {
 
-	var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
-	geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
+			var v1 = vertices[ y ][ x + 1 ];
+			var v2 = vertices[ y ][ x ];
+			var v3 = vertices[ y + 1 ][ x ];
+			var v4 = vertices[ y + 1 ][ x + 1 ];
 
-	for ( var i = 0, il = 8; i < il; i ++ ) {
+			var n1 = this.vertices[ v1 ].clone().normalize();
+			var n2 = this.vertices[ v2 ].clone().normalize();
+			var n3 = this.vertices[ v3 ].clone().normalize();
+			var n4 = this.vertices[ v4 ].clone().normalize();
 
-		geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ];
+			var uv1 = uvs[ y ][ x + 1 ].clone();
+			var uv2 = uvs[ y ][ x ].clone();
+			var uv3 = uvs[ y + 1 ][ x ].clone();
+			var uv4 = uvs[ y + 1 ][ x + 1 ].clone();
 
-	}
+			if ( Math.abs( this.vertices[ v1 ].y ) === radius ) {
 
-	var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } );
+				uv1.x = ( uv1.x + uv2.x ) / 2;
+				this.faces.push( new THREE.Face3( v1, v3, v4, [ n1, n3, n4 ] ) );
+				this.faceVertexUvs[ 0 ].push( [ uv1, uv3, uv4 ] );
 
-	this.lightSphere = new THREE.Mesh( geometry, material );
-	this.add( this.lightSphere );
+			} else if ( Math.abs( this.vertices[ v3 ].y ) === radius ) {
 
-	this.update();
+				uv3.x = ( uv3.x + uv4.x ) / 2;
+				this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
+				this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
 
-};
+			} else {
 
-THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+				this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
+				this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
 
-THREE.HemisphereLightHelper.prototype.dispose = function () {
-	this.lightSphere.geometry.dispose();
-	this.lightSphere.material.dispose();
-};
+				this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) );
+				this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
 
-THREE.HemisphereLightHelper.prototype.update = function () {
+			}
 
-	var vector = new THREE.Vector3();
+		}
 
-	return function () {
+	}
 
-		this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity );
-		this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity );
+	this.computeFaceNormals();
 
-		this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() );
-		this.lightSphere.geometry.colorsNeedUpdate = true;
+	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
 
-	}
+};
 
-}();
+THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-// File:src/extras/helpers/PointLightHelper.js
+// File:src/extras/geometries/TextGeometry.js
 
 /**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
  * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com/
+ *
+ * For creating 3D text geometry in three.js
+ *
+ * Text = 3D Text
+ *
+ * parameters = {
+ *  size: 			<float>, 	// size of the text
+ *  height: 		<float>, 	// thickness to extrude text
+ *  curveSegments: 	<int>,		// number of points on the curves
+ *
+ *  font: 			<string>,		// font name
+ *  weight: 		<string>,		// font weight (normal, bold)
+ *  style: 			<string>,		// font style  (normal, italics)
+ *
+ *  bevelEnabled:	<bool>,			// turn on bevel
+ *  bevelThickness: <float>, 		// how deep into text bevel goes
+ *  bevelSize:		<float>, 		// how far from text outline is bevel
+ *  }
+ *
  */
 
-THREE.PointLightHelper = function ( light, sphereSize ) {
+/*	Usage Examples
 
-	this.light = light;
-	this.light.updateMatrixWorld();
+	// TextGeometry wrapper
 
-	var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
-	var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
-	material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+	var text3d = new TextGeometry( text, options );
 
-	THREE.Mesh.call( this, geometry, material );
+	// Complete manner
 
-	this.matrix = this.light.matrixWorld;
-	this.matrixAutoUpdate = false;
+	var textShapes = THREE.FontUtils.generateShapes( text, options );
+	var text3d = new ExtrudeGeometry( textShapes, options );
 
-	/*
-	var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );
-	var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );
+*/
 
-	this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
-	this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );
 
-	var d = light.distance;
+THREE.TextGeometry = function ( text, parameters ) {
 
-	if ( d === 0.0 ) {
+	parameters = parameters || {};
 
-		this.lightDistance.visible = false;
+	var textShapes = THREE.FontUtils.generateShapes( text, parameters );
 
-	} else {
+	// translate parameters to ExtrudeGeometry API
 
-		this.lightDistance.scale.set( d, d, d );
+	parameters.amount = parameters.height !== undefined ? parameters.height : 50;
 
-	}
+	// defaults
 
-	this.add( this.lightDistance );
-	*/
+	if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
+	if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
+	if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
+
+	THREE.ExtrudeGeometry.call( this, textShapes, parameters );
+
+	this.type = 'TextGeometry';
 
 };
 
-THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype );
+THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype );
 
-THREE.PointLightHelper.prototype.dispose = function () {
+// File:src/extras/geometries/TorusGeometry.js
 
-	this.geometry.dispose();
-	this.material.dispose();
-};
+/**
+ * @author oosmoxiecode
+ * @author mrdoob / http://mrdoob.com/
+ * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888
+ */
 
-THREE.PointLightHelper.prototype.update = function () {
+THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) {
 
-	this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+	THREE.Geometry.call( this );
 
-	/*
-	var d = this.light.distance;
+	this.type = 'TorusGeometry';
 
-	if ( d === 0.0 ) {
+	this.parameters = {
+		radius: radius,
+		tube: tube,
+		radialSegments: radialSegments,
+		tubularSegments: tubularSegments,
+		arc: arc
+	};
 
-		this.lightDistance.visible = false;
+	radius = radius || 100;
+	tube = tube || 40;
+	radialSegments = radialSegments || 8;
+	tubularSegments = tubularSegments || 6;
+	arc = arc || Math.PI * 2;
 
-	} else {
+	var center = new THREE.Vector3(), uvs = [], normals = [];
 
-		this.lightDistance.visible = true;
-		this.lightDistance.scale.set( d, d, d );
+	for ( var j = 0; j <= radialSegments; j ++ ) {
 
-	}
-	*/
+		for ( var i = 0; i <= tubularSegments; i ++ ) {
 
-};
+			var u = i / tubularSegments * arc;
+			var v = j / radialSegments * Math.PI * 2;
 
-// File:src/extras/helpers/SkeletonHelper.js
+			center.x = radius * Math.cos( u );
+			center.y = radius * Math.sin( u );
 
-/**
- * @author Sean Griffin / http://twitter.com/sgrif
- * @author Michael Guerrero / http://realitymeltdown.com
- * @author mrdoob / http://mrdoob.com/
- * @author ikerr / http://verold.com
- */
+			var vertex = new THREE.Vector3();
+			vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );
+			vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );
+			vertex.z = tube * Math.sin( v );
 
-THREE.SkeletonHelper = function ( object ) {
+			this.vertices.push( vertex );
 
-	this.bones = this.getBoneList( object );
+			uvs.push( new THREE.Vector2( i / tubularSegments, j / radialSegments ) );
+			normals.push( vertex.clone().sub( center ).normalize() );
 
-	var geometry = new THREE.Geometry();
+		}
 
-	for ( var i = 0; i < this.bones.length; i ++ ) {
+	}
 
-		var bone = this.bones[ i ];
+	for ( var j = 1; j <= radialSegments; j ++ ) {
 
-		if ( bone.parent instanceof THREE.Bone ) {
+		for ( var i = 1; i <= tubularSegments; i ++ ) {
 
-			geometry.vertices.push( new THREE.Vector3() );
-			geometry.vertices.push( new THREE.Vector3() );
-			geometry.colors.push( new THREE.Color( 0, 0, 1 ) );
-			geometry.colors.push( new THREE.Color( 0, 1, 0 ) );
+			var a = ( tubularSegments + 1 ) * j + i - 1;
+			var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;
+			var c = ( tubularSegments + 1 ) * ( j - 1 ) + i;
+			var d = ( tubularSegments + 1 ) * j + i;
+
+			var face = new THREE.Face3( a, b, d, [ normals[ a ].clone(), normals[ b ].clone(), normals[ d ].clone() ] );
+			this.faces.push( face );
+			this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] );
+
+			face = new THREE.Face3( b, c, d, [ normals[ b ].clone(), normals[ c ].clone(), normals[ d ].clone() ] );
+			this.faces.push( face );
+			this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] );
 
 		}
 
 	}
 
-	var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } );
+	this.computeFaceNormals();
 
-	THREE.Line.call( this, geometry, material, THREE.LinePieces );
+};
 
-	this.root = object;
+THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-	this.matrix = object.matrixWorld;
-	this.matrixAutoUpdate = false;
+// File:src/extras/geometries/TorusKnotGeometry.js
 
-	this.update();
+/**
+ * @author oosmoxiecode
+ * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473
+ */
 
-};
+THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) {
 
+	THREE.Geometry.call( this );
 
-THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype );
+	this.type = 'TorusKnotGeometry';
 
-THREE.SkeletonHelper.prototype.getBoneList = function( object ) {
+	this.parameters = {
+		radius: radius,
+		tube: tube,
+		radialSegments: radialSegments,
+		tubularSegments: tubularSegments,
+		p: p,
+		q: q,
+		heightScale: heightScale
+	};
 
-	var boneList = [];
+	radius = radius || 100;
+	tube = tube || 40;
+	radialSegments = radialSegments || 64;
+	tubularSegments = tubularSegments || 8;
+	p = p || 2;
+	q = q || 3;
+	heightScale = heightScale || 1;
+	
+	var grid = new Array( radialSegments );
+	var tang = new THREE.Vector3();
+	var n = new THREE.Vector3();
+	var bitan = new THREE.Vector3();
 
-	if ( object instanceof THREE.Bone ) {
+	for ( var i = 0; i < radialSegments; ++ i ) {
 
-		boneList.push( object );
+		grid[ i ] = new Array( tubularSegments );
+		var u = i / radialSegments * 2 * p * Math.PI;
+		var p1 = getPos( u, q, p, radius, heightScale );
+		var p2 = getPos( u + 0.01, q, p, radius, heightScale );
+		tang.subVectors( p2, p1 );
+		n.addVectors( p2, p1 );
 
-	}
+		bitan.crossVectors( tang, n );
+		n.crossVectors( bitan, tang );
+		bitan.normalize();
+		n.normalize();
 
-	for ( var i = 0; i < object.children.length; i ++ ) {
+		for ( var j = 0; j < tubularSegments; ++ j ) {
 
-		boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) );
+			var v = j / tubularSegments * 2 * Math.PI;
+			var cx = - tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
+			var cy = tube * Math.sin( v );
 
-	}
+			var pos = new THREE.Vector3();
+			pos.x = p1.x + cx * n.x + cy * bitan.x;
+			pos.y = p1.y + cx * n.y + cy * bitan.y;
+			pos.z = p1.z + cx * n.z + cy * bitan.z;
 
-	return boneList;
+			grid[ i ][ j ] = this.vertices.push( pos ) - 1;
 
-};
+		}
 
-THREE.SkeletonHelper.prototype.update = function () {
+	}
 
-	var geometry = this.geometry;
+	for ( var i = 0; i < radialSegments; ++ i ) {
 
-	var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld );
+		for ( var j = 0; j < tubularSegments; ++ j ) {
 
-	var boneMatrix = new THREE.Matrix4();
+			var ip = ( i + 1 ) % radialSegments;
+			var jp = ( j + 1 ) % tubularSegments;
 
-	var j = 0;
+			var a = grid[ i ][ j ];
+			var b = grid[ ip ][ j ];
+			var c = grid[ ip ][ jp ];
+			var d = grid[ i ][ jp ];
 
-	for ( var i = 0; i < this.bones.length; i ++ ) {
+			var uva = new THREE.Vector2( i / radialSegments, j / tubularSegments );
+			var uvb = new THREE.Vector2( ( i + 1 ) / radialSegments, j / tubularSegments );
+			var uvc = new THREE.Vector2( ( i + 1 ) / radialSegments, ( j + 1 ) / tubularSegments );
+			var uvd = new THREE.Vector2( i / radialSegments, ( j + 1 ) / tubularSegments );
 
-		var bone = this.bones[ i ];
+			this.faces.push( new THREE.Face3( a, b, d ) );
+			this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
 
-		if ( bone.parent instanceof THREE.Bone ) {
+			this.faces.push( new THREE.Face3( b, c, d ) );
+			this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
 
-			boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld );
-			geometry.vertices[ j ].setFromMatrixPosition( boneMatrix );
+		}
+	}
 
-			boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld );
-			geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix );
+	this.computeFaceNormals();
+	this.computeVertexNormals();
 
-			j += 2;
+	function getPos( u, in_q, in_p, radius, heightScale ) {
 
-		}
+		var cu = Math.cos( u );
+		var su = Math.sin( u );
+		var quOverP = in_q / in_p * u;
+		var cs = Math.cos( quOverP );
 
-	}
+		var tx = radius * ( 2 + cs ) * 0.5 * cu;
+		var ty = radius * ( 2 + cs ) * su * 0.5;
+		var tz = heightScale * radius * Math.sin( quOverP ) * 0.5;
 
-	geometry.verticesNeedUpdate = true;
+		return new THREE.Vector3( tx, ty, tz );
 
-	geometry.computeBoundingSphere();
+	}
 
 };
 
-// File:src/extras/helpers/SpotLightHelper.js
+THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+// File:src/extras/geometries/TubeGeometry.js
 
 /**
- * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com/
- * @author WestLangley / http://github.com/WestLangley
-*/
+ * @author WestLangley / https://github.com/WestLangley
+ * @author zz85 / https://github.com/zz85
+ * @author miningold / https://github.com/miningold
+ *
+ * Modified from the TorusKnotGeometry by @oosmoxiecode
+ *
+ * Creates a tube which extrudes along a 3d spline
+ *
+ * Uses parallel transport frames as described in
+ * http://www.cs.indiana.edu/pub/techreports/TR425.pdf
+ */
 
-THREE.SpotLightHelper = function ( light ) {
+THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed ) {
 
-	THREE.Object3D.call( this );
+	THREE.Geometry.call( this );
 
-	this.light = light;
-	this.light.updateMatrixWorld();
+	this.type = 'TubeGeometry';
 
-	this.matrix = light.matrixWorld;
-	this.matrixAutoUpdate = false;
+	this.parameters = {
+		path: path,
+		segments: segments,
+		radius: radius,
+		radialSegments: radialSegments,
+		closed: closed
+	};
 
-	var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true );
+	segments = segments || 64;
+	radius = radius || 1;
+	radialSegments = radialSegments || 8;
+	closed = closed || false;
 
-	geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) );
-	geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
+	var grid = [];
 
-	var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
+	var scope = this,
 
-	this.cone = new THREE.Mesh( geometry, material );
-	this.add( this.cone );
+		tangent,
+		normal,
+		binormal,
 
-	this.update();
+		numpoints = segments + 1,
 
-};
+		x, y, z,
+		tx, ty, tz,
+		u, v,
 
-THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype );
+		cx, cy,
+		pos, pos2 = new THREE.Vector3(),
+		i, j,
+		ip, jp,
+		a, b, c, d,
+		uva, uvb, uvc, uvd;
 
-THREE.SpotLightHelper.prototype.dispose = function () {
-	this.cone.geometry.dispose();
-	this.cone.material.dispose();
-};
+	var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ),
+		tangents = frames.tangents,
+		normals = frames.normals,
+		binormals = frames.binormals;
 
-THREE.SpotLightHelper.prototype.update = function () {
+	// proxy internals
+	this.tangents = tangents;
+	this.normals = normals;
+	this.binormals = binormals;
 
-	var vector = new THREE.Vector3();
-	var vector2 = new THREE.Vector3();
+	function vert( x, y, z ) {
 
-	return function () {
+		return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1;
 
-		var coneLength = this.light.distance ? this.light.distance : 10000;
-		var coneWidth = coneLength * Math.tan( this.light.angle );
+	}
 
-		this.cone.scale.set( coneWidth, coneWidth, coneLength );
+	// consruct the grid
 
-		vector.setFromMatrixPosition( this.light.matrixWorld );
-		vector2.setFromMatrixPosition( this.light.target.matrixWorld );
+	for ( i = 0; i < numpoints; i ++ ) {
 
-		this.cone.lookAt( vector2.sub( vector ) );
+		grid[ i ] = [];
 
-		this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+		u = i / ( numpoints - 1 );
 
-	};
+		pos = path.getPointAt( u );
 
-}();
+		tangent = tangents[ i ];
+		normal = normals[ i ];
+		binormal = binormals[ i ];
 
-// File:src/extras/helpers/VertexNormalsHelper.js
+		for ( j = 0; j < radialSegments; j ++ ) {
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author WestLangley / http://github.com/WestLangley
-*/
+			v = j / radialSegments * 2 * Math.PI;
 
-THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) {
+			cx = - radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
+			cy = radius * Math.sin( v );
 
-	this.object = object;
+			pos2.copy( pos );
+			pos2.x += cx * normal.x + cy * binormal.x;
+			pos2.y += cx * normal.y + cy * binormal.y;
+			pos2.z += cx * normal.z + cy * binormal.z;
 
-	this.size = ( size !== undefined ) ? size : 1;
+			grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );
 
-	var color = ( hex !== undefined ) ? hex : 0xff0000;
+		}
+	}
 
-	var width = ( linewidth !== undefined ) ? linewidth : 1;
 
-	var geometry = new THREE.Geometry();
+	// construct the mesh
 
-	var vertices = object.geometry.vertices;
+	for ( i = 0; i < segments; i ++ ) {
 
-	var faces = object.geometry.faces;
+		for ( j = 0; j < radialSegments; j ++ ) {
 
-	for ( var i = 0, l = faces.length; i < l; i ++ ) {
+			ip = ( closed ) ? (i + 1) % segments : i + 1;
+			jp = (j + 1) % radialSegments;
 
-		var face = faces[ i ];
+			a = grid[ i ][ j ];		// *** NOT NECESSARILY PLANAR ! ***
+			b = grid[ ip ][ j ];
+			c = grid[ ip ][ jp ];
+			d = grid[ i ][ jp ];
 
-		for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
+			uva = new THREE.Vector2( i / segments, j / radialSegments );
+			uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments );
+			uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments );
+			uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments );
 
-			geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() );
+			this.faces.push( new THREE.Face3( a, b, d ) );
+			this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
 
-		}
+			this.faces.push( new THREE.Face3( b, c, d ) );
+			this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
 
+		}
 	}
 
-	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
+	this.computeFaceNormals();
+	this.computeVertexNormals();
 
-	this.matrixAutoUpdate = false;
+};
 
-	this.normalMatrix = new THREE.Matrix3();
+THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-	this.update();
 
-};
+// For computing of Frenet frames, exposing the tangents, normals and binormals the spline
+THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) {
 
-THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype );
+	var	tangent = new THREE.Vector3(),
+		normal = new THREE.Vector3(),
+		binormal = new THREE.Vector3(),
 
-THREE.VertexNormalsHelper.prototype.update = ( function ( object ) {
+		tangents = [],
+		normals = [],
+		binormals = [],
 
-	var v1 = new THREE.Vector3();
+		vec = new THREE.Vector3(),
+		mat = new THREE.Matrix4(),
 
-	return function( object ) {
+		numpoints = segments + 1,
+		theta,
+		epsilon = 0.0001,
+		smallest,
 
-		var keys = [ 'a', 'b', 'c', 'd' ];
+		tx, ty, tz,
+		i, u, v;
 
-		this.object.updateMatrixWorld( true );
 
-		this.normalMatrix.getNormalMatrix( this.object.matrixWorld );
+	// expose internals
+	this.tangents = tangents;
+	this.normals = normals;
+	this.binormals = binormals;
 
-		var vertices = this.geometry.vertices;
+	// compute the tangent vectors for each segment on the path
 
-		var verts = this.object.geometry.vertices;
+	for ( i = 0; i < numpoints; i ++ ) {
 
-		var faces = this.object.geometry.faces;
+		u = i / ( numpoints - 1 );
 
-		var worldMatrix = this.object.matrixWorld;
+		tangents[ i ] = path.getTangentAt( u );
+		tangents[ i ].normalize();
 
-		var idx = 0;
+	}
 
-		for ( var i = 0, l = faces.length; i < l; i ++ ) {
+	initialNormal3();
 
-			var face = faces[ i ];
+	/*
+	function initialNormal1(lastBinormal) {
+		// fixed start binormal. Has dangers of 0 vectors
+		normals[ 0 ] = new THREE.Vector3();
+		binormals[ 0 ] = new THREE.Vector3();
+		if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 );
+		normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize();
+		binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
+	}
 
-			for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
+	function initialNormal2() {
 
-				var vertexId = face[ keys[ j ] ];
-				var vertex = verts[ vertexId ];
+		// This uses the Frenet-Serret formula for deriving binormal
+		var t2 = path.getTangentAt( epsilon );
 
-				var normal = face.vertexNormals[ j ];
+		normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize();
+		binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] );
 
-				vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix );
+		normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent
+		binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
 
-				v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size );
+	}
+	*/
 
-				v1.add( vertices[ idx ] );
-				idx = idx + 1;
+	function initialNormal3() {
+		// select an initial normal vector perpenicular to the first tangent vector,
+		// and in the direction of the smallest tangent xyz component
 
-				vertices[ idx ].copy( v1 );
-				idx = idx + 1;
+		normals[ 0 ] = new THREE.Vector3();
+		binormals[ 0 ] = new THREE.Vector3();
+		smallest = Number.MAX_VALUE;
+		tx = Math.abs( tangents[ 0 ].x );
+		ty = Math.abs( tangents[ 0 ].y );
+		tz = Math.abs( tangents[ 0 ].z );
 
-			}
+		if ( tx <= smallest ) {
+			smallest = tx;
+			normal.set( 1, 0, 0 );
+		}
 
+		if ( ty <= smallest ) {
+			smallest = ty;
+			normal.set( 0, 1, 0 );
 		}
 
-		this.geometry.verticesNeedUpdate = true;
+		if ( tz <= smallest ) {
+			normal.set( 0, 0, 1 );
+		}
 
-		return this;
+		vec.crossVectors( tangents[ 0 ], normal ).normalize();
 
+		normals[ 0 ].crossVectors( tangents[ 0 ], vec );
+		binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );
 	}
 
-}());
-
-// File:src/extras/helpers/VertexTangentsHelper.js
 
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author WestLangley / http://github.com/WestLangley
-*/
+	// compute the slowly-varying normal and binormal vectors for each segment on the path
 
-THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) {
+	for ( i = 1; i < numpoints; i ++ ) {
 
-	this.object = object;
+		normals[ i ] = normals[ i-1 ].clone();
 
-	this.size = ( size !== undefined ) ? size : 1;
+		binormals[ i ] = binormals[ i-1 ].clone();
 
-	var color = ( hex !== undefined ) ? hex : 0x0000ff;
+		vec.crossVectors( tangents[ i-1 ], tangents[ i ] );
 
-	var width = ( linewidth !== undefined ) ? linewidth : 1;
+		if ( vec.length() > epsilon ) {
 
-	var geometry = new THREE.Geometry();
+			vec.normalize();
 
-	var vertices = object.geometry.vertices;
+			theta = Math.acos( THREE.Math.clamp( tangents[ i-1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors
 
-	var faces = object.geometry.faces;
+			normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
 
-	for ( var i = 0, l = faces.length; i < l; i ++ ) {
+		}
 
-		var face = faces[ i ];
+		binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
 
-		for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) {
+	}
 
-			geometry.vertices.push( new THREE.Vector3() );
-			geometry.vertices.push( new THREE.Vector3() );
 
-		}
+	// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
 
-	}
+	if ( closed ) {
 
-	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
+		theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints-1 ] ), - 1, 1 ) );
+		theta /= ( numpoints - 1 );
 
-	this.matrixAutoUpdate = false;
+		if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) {
 
-	this.update();
+			theta = - theta;
 
-};
+		}
 
-THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype );
+		for ( i = 1; i < numpoints; i ++ ) {
 
-THREE.VertexTangentsHelper.prototype.update = ( function ( object ) {
+			// twist a little...
+			normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
+			binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
 
-	var v1 = new THREE.Vector3();
+		}
 
-	return function( object ) {
+	}
+};
 
-		var keys = [ 'a', 'b', 'c', 'd' ];
+// File:src/extras/geometries/PolyhedronGeometry.js
 
-		this.object.updateMatrixWorld( true );
+/**
+ * @author clockworkgeek / https://github.com/clockworkgeek
+ * @author timothypratley / https://github.com/timothypratley
+ * @author WestLangley / http://github.com/WestLangley
+*/
 
-		var vertices = this.geometry.vertices;
+THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) {
 
-		var verts = this.object.geometry.vertices;
+	THREE.Geometry.call( this );
 
-		var faces = this.object.geometry.faces;
+	this.type = 'PolyhedronGeometry';
 
-		var worldMatrix = this.object.matrixWorld;
+	this.parameters = {
+		vertices: vertices,
+		indices: indices,
+		radius: radius,
+		detail: detail
+	};
 
-		var idx = 0;
+	radius = radius || 1;
+	detail = detail || 0;
 
-		for ( var i = 0, l = faces.length; i < l; i ++ ) {
+	var that = this;
 
-			var face = faces[ i ];
+	for ( var i = 0, l = vertices.length; i < l; i += 3 ) {
 
-			for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) {
+		prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) );
 
-				var vertexId = face[ keys[ j ] ];
-				var vertex = verts[ vertexId ];
+	}
 
-				var tangent = face.vertexTangents[ j ];
+	var midpoints = [], p = this.vertices;
 
-				vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix );
+	var faces = [];
 
-				v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size );
+	for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) {
 
-				v1.add( vertices[ idx ] );
-				idx = idx + 1;
+		var v1 = p[ indices[ i     ] ];
+		var v2 = p[ indices[ i + 1 ] ];
+		var v3 = p[ indices[ i + 2 ] ];
 
-				vertices[ idx ].copy( v1 );
-				idx = idx + 1;
+		faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] );
 
-			}
+	}
 
-		}
+	var centroid = new THREE.Vector3();
 
-		this.geometry.verticesNeedUpdate = true;
+	for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-		return this;
+		subdivide( faces[ i ], detail );
 
 	}
 
-}());
-
-// File:src/extras/helpers/WireframeHelper.js
-
-/**
- * @author mrdoob / http://mrdoob.com/
- */
 
-THREE.WireframeHelper = function ( object, hex ) {
+	// Handle case when face straddles the seam
 
-	var color = ( hex !== undefined ) ? hex : 0xffffff;
+	for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) {
 
-	var edge = [ 0, 0 ], hash = {};
-	var sortFunction = function ( a, b ) { return a - b };
+		var uvs = this.faceVertexUvs[ 0 ][ i ];
 
-	var keys = [ 'a', 'b', 'c' ];
-	var geometry = new THREE.BufferGeometry();
+		var x0 = uvs[ 0 ].x;
+		var x1 = uvs[ 1 ].x;
+		var x2 = uvs[ 2 ].x;
 
-	if ( object.geometry instanceof THREE.Geometry ) {
+		var max = Math.max( x0, Math.max( x1, x2 ) );
+		var min = Math.min( x0, Math.min( x1, x2 ) );
 
-		var vertices = object.geometry.vertices;
-		var faces = object.geometry.faces;
-		var numEdges = 0;
+		if ( max > 0.9 && min < 0.1 ) { // 0.9 is somewhat arbitrary
 
-		// allocate maximal size
-		var edges = new Uint32Array( 6 * faces.length );
+			if ( x0 < 0.2 ) uvs[ 0 ].x += 1;
+			if ( x1 < 0.2 ) uvs[ 1 ].x += 1;
+			if ( x2 < 0.2 ) uvs[ 2 ].x += 1;
 
-		for ( var i = 0, l = faces.length; i < l; i ++ ) {
+		}
 
-			var face = faces[ i ];
+	}
 
-			for ( var j = 0; j < 3; j ++ ) {
 
-				edge[ 0 ] = face[ keys[ j ] ];
-				edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
-				edge.sort( sortFunction );
+	// Apply radius
 
-				var key = edge.toString();
+	for ( var i = 0, l = this.vertices.length; i < l; i ++ ) {
 
-				if ( hash[ key ] === undefined ) {
+		this.vertices[ i ].multiplyScalar( radius );
 
-					edges[ 2 * numEdges ] = edge[ 0 ];
-					edges[ 2 * numEdges + 1 ] = edge[ 1 ];
-					hash[ key ] = true;
-					numEdges ++;
+	}
 
-				}
 
-			}
+	// Merge vertices
 
-		}
+	this.mergeVertices();
 
-		var coords = new Float32Array( numEdges * 2 * 3 );
+	this.computeFaceNormals();
 
-		for ( var i = 0, l = numEdges; i < l; i ++ ) {
+	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
 
-			for ( var j = 0; j < 2; j ++ ) {
 
-				var vertex = vertices[ edges [ 2 * i + j] ];
+	// Project vector onto sphere's surface
 
-				var index = 6 * i + 3 * j;
-				coords[ index + 0 ] = vertex.x;
-				coords[ index + 1 ] = vertex.y;
-				coords[ index + 2 ] = vertex.z;
+	function prepare( vector ) {
 
-			}
+		var vertex = vector.normalize().clone();
+		vertex.index = that.vertices.push( vertex ) - 1;
 
-		}
+		// Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle.
 
-		geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
+		var u = azimuth( vector ) / 2 / Math.PI + 0.5;
+		var v = inclination( vector ) / Math.PI + 0.5;
+		vertex.uv = new THREE.Vector2( u, 1 - v );
 
-	} else if ( object.geometry instanceof THREE.BufferGeometry ) {
+		return vertex;
 
-		if ( object.geometry.attributes.index !== undefined ) { // Indexed BufferGeometry
+	}
 
-			var vertices = object.geometry.attributes.position.array;
-			var indices = object.geometry.attributes.index.array;
-			var drawcalls = object.geometry.drawcalls;
-			var numEdges = 0;
 
-			if ( drawcalls.length === 0 ) {
+	// Approximate a curved face with recursively sub-divided triangles.
 
-				drawcalls = [ { count : indices.length, index : 0, start : 0 } ];
+	function make( v1, v2, v3 ) {
 
-			}
+		var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] );
+		that.faces.push( face );
 
-			// allocate maximal size
-			var edges = new Uint32Array( 2 * indices.length );
+		centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 );
 
-			for ( var o = 0, ol = drawcalls.length; o < ol; ++ o ) {
+		var azi = azimuth( centroid );
 
-				var start = drawcalls[ o ].start;
-				var count = drawcalls[ o ].count;
-				var index = drawcalls[ o ].index;
+		that.faceVertexUvs[ 0 ].push( [
+			correctUV( v1.uv, v1, azi ),
+			correctUV( v2.uv, v2, azi ),
+			correctUV( v3.uv, v3, azi )
+		] );
 
-				for ( var i = start, il = start + count; i < il; i += 3 ) {
+	}
 
-					for ( var j = 0; j < 3; j ++ ) {
 
-						edge[ 0 ] = index + indices[ i + j ];
-						edge[ 1 ] = index + indices[ i + ( j + 1 ) % 3 ];
-						edge.sort( sortFunction );
+	// Analytically subdivide a face to the required detail level.
 
-						var key = edge.toString();
+	function subdivide( face, detail ) {
 
-						if ( hash[ key ] === undefined ) {
+		var cols = Math.pow(2, detail);
+		var cells = Math.pow(4, detail);
+		var a = prepare( that.vertices[ face.a ] );
+		var b = prepare( that.vertices[ face.b ] );
+		var c = prepare( that.vertices[ face.c ] );
+		var v = [];
 
-							edges[ 2 * numEdges ] = edge[ 0 ];
-							edges[ 2 * numEdges + 1 ] = edge[ 1 ];
-							hash[ key ] = true;
-							numEdges ++;
+		// Construct all of the vertices for this subdivision.
 
-						}
+		for ( var i = 0 ; i <= cols; i ++ ) {
 
-					}
+			v[ i ] = [];
 
-				}
+			var aj = prepare( a.clone().lerp( c, i / cols ) );
+			var bj = prepare( b.clone().lerp( c, i / cols ) );
+			var rows = cols - i;
 
-			}
+			for ( var j = 0; j <= rows; j ++) {
 
-			var coords = new Float32Array( numEdges * 2 * 3 );
+				if ( j == 0 && i == cols ) {
 
-			for ( var i = 0, l = numEdges; i < l; i ++ ) {
+					v[ i ][ j ] = aj;
 
-				for ( var j = 0; j < 2; j ++ ) {
+				} else {
 
-					var index = 6 * i + 3 * j;
-					var index2 = 3 * edges[ 2 * i + j];
-					coords[ index + 0 ] = vertices[ index2 ];
-					coords[ index + 1 ] = vertices[ index2 + 1 ];
-					coords[ index + 2 ] = vertices[ index2 + 2 ];
+					v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) );
 
 				}
 
 			}
 
-			geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
+		}
 
-		} else { // non-indexed BufferGeometry
+		// Construct all of the faces.
 
-			var vertices = object.geometry.attributes.position.array;
-			var numEdges = vertices.length / 3;
-			var numTris = numEdges / 3;
+		for ( var i = 0; i < cols ; i ++ ) {
 
-			var coords = new Float32Array( numEdges * 2 * 3 );
+			for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) {
 
-			for ( var i = 0, l = numTris; i < l; i ++ ) {
+				var k = Math.floor( j / 2 );
 
-				for ( var j = 0; j < 3; j ++ ) {
+				if ( j % 2 == 0 ) {
 
-					var index = 18 * i + 6 * j;
+					make(
+						v[ i ][ k + 1],
+						v[ i + 1 ][ k ],
+						v[ i ][ k ]
+					);
 
-					var index1 = 9 * i + 3 * j;
-					coords[ index + 0 ] = vertices[ index1 ];
-					coords[ index + 1 ] = vertices[ index1 + 1 ];
-					coords[ index + 2 ] = vertices[ index1 + 2 ];
+				} else {
 
-					var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 );
-					coords[ index + 3 ] = vertices[ index2 ];
-					coords[ index + 4 ] = vertices[ index2 + 1 ];
-					coords[ index + 5 ] = vertices[ index2 + 2 ];
+					make(
+						v[ i ][ k + 1 ],
+						v[ i + 1][ k + 1],
+						v[ i + 1 ][ k ]
+					);
 
 				}
 
 			}
 
-			geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
-
 		}
 
 	}
 
-	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces );
 
-	this.matrix = object.matrixWorld;
-	this.matrixAutoUpdate = false;
+	// Angle around the Y axis, counter-clockwise when looking from above.
 
-};
+	function azimuth( vector ) {
 
-THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype );
+		return Math.atan2( vector.z, - vector.x );
 
-// File:src/extras/objects/ImmediateRenderObject.js
+	}
 
-/**
- * @author alteredq / http://alteredqualia.com/
- */
 
-THREE.ImmediateRenderObject = function () {
+	// Angle above the XZ plane.
 
-	THREE.Object3D.call( this );
+	function inclination( vector ) {
 
-	this.render = function ( renderCallback ) {};
+		return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );
 
-};
+	}
 
-THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype );
 
-// File:src/extras/objects/LensFlare.js
+	// Texture fixing helper. Spheres have some odd behaviours.
 
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- */
+	function correctUV( uv, vector, azimuth ) {
 
-THREE.LensFlare = function ( texture, size, distance, blending, color ) {
+		if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y );
+		if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y );
+		return uv.clone();
 
-	THREE.Object3D.call( this );
+	}
 
-	this.lensFlares = [];
 
-	this.positionScreen = new THREE.Vector3();
-	this.customUpdateCallback = undefined;
+};
 
-	if( texture !== undefined ) {
+THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-		this.add( texture, size, distance, blending, color );
+// File:src/extras/geometries/DodecahedronGeometry.js
 
-	}
+/**
+ * @author Abe Pazos / https://hamoid.com
+ */
 
-};
+THREE.DodecahedronGeometry = function ( radius, detail ) {
 
-THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype );
+	this.parameters = {
+		radius: radius,
+		detail: detail
+	};
 
+	var t = ( 1 + Math.sqrt( 5 ) ) / 2;
+	var r = 1 / t;
 
-/*
- * Add: adds another flare
- */
+	var vertices = [
 
-THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) {
+		// (±1, ±1, ±1)
+		-1, -1, -1,    -1, -1,  1,
+		-1,  1, -1,    -1,  1,  1,
+		 1, -1, -1,     1, -1,  1,
+		 1,  1, -1,     1,  1,  1,
 
-	if ( size === undefined ) size = - 1;
-	if ( distance === undefined ) distance = 0;
-	if ( opacity === undefined ) opacity = 1;
-	if ( color === undefined ) color = new THREE.Color( 0xffffff );
-	if ( blending === undefined ) blending = THREE.NormalBlending;
+		// (0, ±1/φ, ±φ)
+		 0, -r, -t,     0, -r,  t,
+		 0,  r, -t,     0,  r,  t,
 
-	distance = Math.min( distance, Math.max( 0, distance ) );
+		// (±1/φ, ±φ, 0)
+		-r, -t,  0,    -r,  t,  0,
+		 r, -t,  0,     r,  t,  0,
 
-	this.lensFlares.push( {
-		texture: texture, 			// THREE.Texture
-		size: size, 				// size in pixels (-1 = use texture.width)
-		distance: distance, 		// distance (0-1) from light source (0=at light source)
-		x: 0, y: 0, z: 0,			// screen position (-1 => 1) z = 0 is ontop z = 1 is back
-		scale: 1, 					// scale
-		rotation: 1, 				// rotation
-		opacity: opacity,			// opacity
-		color: color,				// color
-		blending: blending			// blending
-	} );
+		// (±φ, 0, ±1/φ)
+		-t,  0, -r,     t,  0, -r,
+		-t,  0,  r,     t,  0,  r
+	];
+
+	var indices = [
+		 3, 11,  7,      3,  7, 15,      3, 15, 13,
+		 7, 19, 17,      7, 17,  6,      7,  6, 15,
+		17,  4,  8,     17,  8, 10,     17, 10,  6,
+		 8,  0, 16,      8, 16,  2,      8,  2, 10,
+		 0, 12,  1,      0,  1, 18,      0, 18, 16,
+		 6, 10,  2,      6,  2, 13,      6, 13, 15,
+		 2, 16, 18,      2, 18,  3,      2,  3, 13,
+		18,  1,  9,     18,  9, 11,     18, 11,  3,
+		 4, 14, 12,      4, 12,  0,      4,  0,  8,
+		11,  9,  5,     11,  5, 19,     11, 19,  7,
+		19,  5, 14,     19, 14,  4,     19,  4, 17,
+		 1, 12, 14,      1, 14,  5,      1,  5,  9
+	];
+
+	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
 
 };
 
-/*
- * Update lens flares update positions on all flares based on the screen position
- * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.
- */
+THREE.DodecahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-THREE.LensFlare.prototype.updateLensFlares = function () {
+// File:src/extras/geometries/IcosahedronGeometry.js
 
-	var f, fl = this.lensFlares.length;
-	var flare;
-	var vecX = - this.positionScreen.x * 2;
-	var vecY = - this.positionScreen.y * 2;
+/**
+ * @author timothypratley / https://github.com/timothypratley
+ */
 
-	for( f = 0; f < fl; f ++ ) {
+THREE.IcosahedronGeometry = function ( radius, detail ) {
 
-		flare = this.lensFlares[ f ];
+	var t = ( 1 + Math.sqrt( 5 ) ) / 2;
 
-		flare.x = this.positionScreen.x + vecX * flare.distance;
-		flare.y = this.positionScreen.y + vecY * flare.distance;
+	var vertices = [
+		- 1,  t,  0,    1,  t,  0,   - 1, - t,  0,    1, - t,  0,
+		 0, - 1,  t,    0,  1,  t,    0, - 1, - t,    0,  1, - t,
+		 t,  0, - 1,    t,  0,  1,   - t,  0, - 1,   - t,  0,  1
+	];
 
-		flare.wantedRotation = flare.x * Math.PI * 0.25;
-		flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;
+	var indices = [
+		 0, 11,  5,    0,  5,  1,    0,  1,  7,    0,  7, 10,    0, 10, 11,
+		 1,  5,  9,    5, 11,  4,   11, 10,  2,   10,  7,  6,    7,  1,  8,
+		 3,  9,  4,    3,  4,  2,    3,  2,  6,    3,  6,  8,    3,  8,  9,
+		 4,  9,  5,    2,  4, 11,    6,  2, 10,    8,  6,  7,    9,  8,  1
+	];
 
-	}
+	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
+
+	this.type = 'IcosahedronGeometry';
 
+	this.parameters = {
+		radius: radius,
+		detail: detail
+	};
 };
 
+THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-// File:src/extras/objects/MorphBlendMesh.js
+// File:src/extras/geometries/OctahedronGeometry.js
 
 /**
- * @author alteredq / http://alteredqualia.com/
+ * @author timothypratley / https://github.com/timothypratley
  */
 
-THREE.MorphBlendMesh = function( geometry, material ) {
-
-	THREE.Mesh.call( this, geometry, material );
-
-	this.animationsMap = {};
-	this.animationsList = [];
-
-	// prepare default animation
-	// (all frames played together in 1 second)
+THREE.OctahedronGeometry = function ( radius, detail ) {
 
-	var numFrames = this.geometry.morphTargets.length;
+	this.parameters = {
+		radius: radius,
+		detail: detail
+	};
 
-	var name = "__default";
+	var vertices = [
+		1, 0, 0,   - 1, 0, 0,    0, 1, 0,    0,- 1, 0,    0, 0, 1,    0, 0,- 1
+	];
 
-	var startFrame = 0;
-	var endFrame = numFrames - 1;
+	var indices = [
+		0, 2, 4,    0, 4, 3,    0, 3, 5,    0, 5, 2,    1, 2, 5,    1, 5, 3,    1, 3, 4,    1, 4, 2
+	];
 
-	var fps = numFrames / 1;
+	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
 
-	this.createAnimation( name, startFrame, endFrame, fps );
-	this.setAnimationWeight( name, 1 );
+	this.type = 'OctahedronGeometry';
 
+	this.parameters = {
+		radius: radius,
+		detail: detail
+	};
 };
 
-THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype );
+THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) {
+// File:src/extras/geometries/TetrahedronGeometry.js
 
-	var animation = {
+/**
+ * @author timothypratley / https://github.com/timothypratley
+ */
 
-		startFrame: start,
-		endFrame: end,
+THREE.TetrahedronGeometry = function ( radius, detail ) {
 
-		length: end - start + 1,
+	var vertices = [
+		 1,  1,  1,   - 1, - 1,  1,   - 1,  1, - 1,    1, - 1, - 1
+	];
 
-		fps: fps,
-		duration: ( end - start ) / fps,
+	var indices = [
+		 2,  1,  0,    0,  3,  2,    1,  3,  0,    2,  3,  1
+	];
 
-		lastFrame: 0,
-		currentFrame: 0,
+	THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
 
-		active: false,
+	this.type = 'TetrahedronGeometry';
 
-		time: 0,
-		direction: 1,
-		weight: 1,
+	this.parameters = {
+		radius: radius,
+		detail: detail
+	};
 
-		directionBackwards: false,
-		mirroredLoop: false
+};
 
-	};
+THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-	this.animationsMap[ name ] = animation;
-	this.animationsList.push( animation );
+// File:src/extras/geometries/ParametricGeometry.js
 
-};
+/**
+ * @author zz85 / https://github.com/zz85
+ * Parametric Surfaces Geometry
+ * based on the brilliant article by @prideout http://prideout.net/blog/?p=44
+ *
+ * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements );
+ *
+ */
 
-THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) {
+THREE.ParametricGeometry = function ( func, slices, stacks ) {
 
-	var pattern = /([a-z]+)_?(\d+)/;
+	THREE.Geometry.call( this );
 
-	var firstAnimation, frameRanges = {};
+	this.type = 'ParametricGeometry';
 
-	var geometry = this.geometry;
+	this.parameters = {
+		func: func,
+		slices: slices,
+		stacks: stacks
+	};
 
-	for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
+	var verts = this.vertices;
+	var faces = this.faces;
+	var uvs = this.faceVertexUvs[ 0 ];
 
-		var morph = geometry.morphTargets[ i ];
-		var chunks = morph.name.match( pattern );
+	var i, il, j, p;
+	var u, v;
 
-		if ( chunks && chunks.length > 1 ) {
+	var stackCount = stacks + 1;
+	var sliceCount = slices + 1;
 
-			var name = chunks[ 1 ];
-			var num = chunks[ 2 ];
+	for ( i = 0; i <= stacks; i ++ ) {
 
-			if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity };
+		v = i / stacks;
 
-			var range = frameRanges[ name ];
+		for ( j = 0; j <= slices; j ++ ) {
 
-			if ( i < range.start ) range.start = i;
-			if ( i > range.end ) range.end = i;
+			u = j / slices;
 
-			if ( ! firstAnimation ) firstAnimation = name;
+			p = func( u, v );
+			verts.push( p );
 
 		}
-
 	}
 
-	for ( var name in frameRanges ) {
-
-		var range = frameRanges[ name ];
-		this.createAnimation( name, range.start, range.end, fps );
+	var a, b, c, d;
+	var uva, uvb, uvc, uvd;
 
-	}
+	for ( i = 0; i < stacks; i ++ ) {
 
-	this.firstAnimation = firstAnimation;
+		for ( j = 0; j < slices; j ++ ) {
 
-};
+			a = i * sliceCount + j;
+			b = i * sliceCount + j + 1;
+			c = (i + 1) * sliceCount + j + 1;
+			d = (i + 1) * sliceCount + j;
 
-THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) {
+			uva = new THREE.Vector2( j / slices, i / stacks );
+			uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks );
+			uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks );
+			uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks );
 
-	var animation = this.animationsMap[ name ];
+			faces.push( new THREE.Face3( a, b, d ) );
+			uvs.push( [ uva, uvb, uvd ] );
 
-	if ( animation ) {
+			faces.push( new THREE.Face3( b, c, d ) );
+			uvs.push( [ uvb.clone(), uvc, uvd.clone() ] );
 
-		animation.direction = 1;
-		animation.directionBackwards = false;
+		}
 
 	}
 
-};
+	// console.log(this);
 
-THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) {
+	// magic bullet
+	// var diff = this.mergeVertices();
+	// console.log('removed ', diff, ' vertices by merging');
 
-	var animation = this.animationsMap[ name ];
+	this.computeFaceNormals();
+	this.computeVertexNormals();
 
-	if ( animation ) {
+};
 
-		animation.direction = - 1;
-		animation.directionBackwards = true;
+THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype );
 
-	}
+// File:src/extras/helpers/AxisHelper.js
 
-};
+/**
+ * @author sroucheray / http://sroucheray.org/
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) {
+THREE.AxisHelper = function ( size ) {
 
-	var animation = this.animationsMap[ name ];
+	size = size || 1;
 
-	if ( animation ) {
+	var vertices = new Float32Array( [
+		0, 0, 0,  size, 0, 0,
+		0, 0, 0,  0, size, 0,
+		0, 0, 0,  0, 0, size
+	] );
 
-		animation.fps = fps;
-		animation.duration = ( animation.end - animation.start ) / animation.fps;
+	var colors = new Float32Array( [
+		1, 0, 0,  1, 0.6, 0,
+		0, 1, 0,  0.6, 1, 0,
+		0, 0, 1,  0, 0.6, 1
+	] );
 
-	}
+	var geometry = new THREE.BufferGeometry();
+	geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+	geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
 
-};
+	var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
 
-THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) {
+	THREE.Line.call( this, geometry, material, THREE.LinePieces );
 
-	var animation = this.animationsMap[ name ];
+};
 
-	if ( animation ) {
+THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype );
 
-		animation.duration = duration;
-		animation.fps = ( animation.end - animation.start ) / animation.duration;
+// File:src/extras/helpers/ArrowHelper.js
 
-	}
+/**
+ * @author WestLangley / http://github.com/WestLangley
+ * @author zz85 / http://github.com/zz85
+ * @author bhouston / http://exocortex.com
+ *
+ * Creates an arrow for visualizing directions
+ *
+ * Parameters:
+ *  dir - Vector3
+ *  origin - Vector3
+ *  length - Number
+ *  color - color in hex value
+ *  headLength - Number
+ *  headWidth - Number
+ */
 
-};
+THREE.ArrowHelper = ( function () {
 
-THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) {
+	var lineGeometry = new THREE.Geometry();
+	lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) );
 
-	var animation = this.animationsMap[ name ];
+	var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 );
+	coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) );
 
-	if ( animation ) {
+	return function ( dir, origin, length, color, headLength, headWidth ) {
 
-		animation.weight = weight;
+		// dir is assumed to be normalized
 
-	}
+		THREE.Object3D.call( this );
 
-};
+		if ( color === undefined ) color = 0xffff00;
+		if ( length === undefined ) length = 1;
+		if ( headLength === undefined ) headLength = 0.2 * length;
+		if ( headWidth === undefined ) headWidth = 0.2 * headLength;
 
-THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) {
+		this.position.copy( origin );
 
-	var animation = this.animationsMap[ name ];
+		this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) );
+		this.line.matrixAutoUpdate = false;
+		this.add( this.line );
 
-	if ( animation ) {
+		this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) );
+		this.cone.matrixAutoUpdate = false;
+		this.add( this.cone );
 
-		animation.time = time;
+		this.setDirection( dir );
+		this.setLength( length, headLength, headWidth );
 
 	}
 
-};
+}() );
 
-THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) {
+THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype );
 
-	var time = 0;
+THREE.ArrowHelper.prototype.setDirection = ( function () {
 
-	var animation = this.animationsMap[ name ];
+	var axis = new THREE.Vector3();
+	var radians;
 
-	if ( animation ) {
+	return function ( dir ) {
 
-		time = animation.time;
+		// dir is assumed to be normalized
 
-	}
+		if ( dir.y > 0.99999 ) {
 
-	return time;
+			this.quaternion.set( 0, 0, 0, 1 );
 
-};
+		} else if ( dir.y < - 0.99999 ) {
 
-THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) {
+			this.quaternion.set( 1, 0, 0, 0 );
 
-	var duration = - 1;
+		} else {
 
-	var animation = this.animationsMap[ name ];
+			axis.set( dir.z, 0, - dir.x ).normalize();
 
-	if ( animation ) {
+			radians = Math.acos( dir.y );
 
-		duration = animation.duration;
+			this.quaternion.setFromAxisAngle( axis, radians );
 
-	}
+		}
 
-	return duration;
+	};
 
-};
+}() );
 
-THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) {
+THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) {
 
-	var animation = this.animationsMap[ name ];
+	if ( headLength === undefined ) headLength = 0.2 * length;
+	if ( headWidth === undefined ) headWidth = 0.2 * headLength;
 
-	if ( animation ) {
+	this.line.scale.set( 1, length, 1 );
+	this.line.updateMatrix();
 
-		animation.time = 0;
-		animation.active = true;
+	this.cone.scale.set( headWidth, headLength, headWidth );
+	this.cone.position.y = length;
+	this.cone.updateMatrix();
 
-	} else {
+};
 
-		console.warn( "animation[" + name + "] undefined" );
+THREE.ArrowHelper.prototype.setColor = function ( color ) {
 
-	}
+	this.line.material.color.set( color );
+	this.cone.material.color.set( color );
 
 };
 
-THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) {
+// File:src/extras/helpers/BoxHelper.js
 
-	var animation = this.animationsMap[ name ];
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-	if ( animation ) {
+THREE.BoxHelper = function ( object ) {
 
-		animation.active = false;
+	var geometry = new THREE.BufferGeometry();
+	geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( 72 ), 3 ) );
 
-	}
+	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ), THREE.LinePieces );
 
-};
+	if ( object !== undefined ) {
 
-THREE.MorphBlendMesh.prototype.update = function ( delta ) {
+		this.update( object );
 
-	for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) {
+	}
 
-		var animation = this.animationsList[ i ];
+};
 
-		if ( ! animation.active ) continue;
+THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype );
 
-		var frameTime = animation.duration / animation.length;
+THREE.BoxHelper.prototype.update = function ( object ) {
 
-		animation.time += animation.direction * delta;
+	var geometry = object.geometry;
 
-		if ( animation.mirroredLoop ) {
+	if ( geometry.boundingBox === null ) {
 
-			if ( animation.time > animation.duration || animation.time < 0 ) {
+		geometry.computeBoundingBox();
 
-				animation.direction *= - 1;
+	}
 
-				if ( animation.time > animation.duration ) {
+	var min = geometry.boundingBox.min;
+	var max = geometry.boundingBox.max;
 
-					animation.time = animation.duration;
-					animation.directionBackwards = true;
+	/*
+	  5____4
+	1/___0/|
+	| 6__|_7
+	2/___3/
 
-				}
+	0: max.x, max.y, max.z
+	1: min.x, max.y, max.z
+	2: min.x, min.y, max.z
+	3: max.x, min.y, max.z
+	4: max.x, max.y, min.z
+	5: min.x, max.y, min.z
+	6: min.x, min.y, min.z
+	7: max.x, min.y, min.z
+	*/
 
-				if ( animation.time < 0 ) {
+	var vertices = this.geometry.attributes.position.array;
 
-					animation.time = 0;
-					animation.directionBackwards = false;
+	vertices[  0 ] = max.x; vertices[  1 ] = max.y; vertices[  2 ] = max.z;
+	vertices[  3 ] = min.x; vertices[  4 ] = max.y; vertices[  5 ] = max.z;
 
-				}
+	vertices[  6 ] = min.x; vertices[  7 ] = max.y; vertices[  8 ] = max.z;
+	vertices[  9 ] = min.x; vertices[ 10 ] = min.y; vertices[ 11 ] = max.z;
 
-			}
+	vertices[ 12 ] = min.x; vertices[ 13 ] = min.y; vertices[ 14 ] = max.z;
+	vertices[ 15 ] = max.x; vertices[ 16 ] = min.y; vertices[ 17 ] = max.z;
 
-		} else {
+	vertices[ 18 ] = max.x; vertices[ 19 ] = min.y; vertices[ 20 ] = max.z;
+	vertices[ 21 ] = max.x; vertices[ 22 ] = max.y; vertices[ 23 ] = max.z;
 
-			animation.time = animation.time % animation.duration;
+	//
 
-			if ( animation.time < 0 ) animation.time += animation.duration;
+	vertices[ 24 ] = max.x; vertices[ 25 ] = max.y; vertices[ 26 ] = min.z;
+	vertices[ 27 ] = min.x; vertices[ 28 ] = max.y; vertices[ 29 ] = min.z;
 
-		}
+	vertices[ 30 ] = min.x; vertices[ 31 ] = max.y; vertices[ 32 ] = min.z;
+	vertices[ 33 ] = min.x; vertices[ 34 ] = min.y; vertices[ 35 ] = min.z;
 
-		var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 );
-		var weight = animation.weight;
+	vertices[ 36 ] = min.x; vertices[ 37 ] = min.y; vertices[ 38 ] = min.z;
+	vertices[ 39 ] = max.x; vertices[ 40 ] = min.y; vertices[ 41 ] = min.z;
 
-		if ( keyframe !== animation.currentFrame ) {
+	vertices[ 42 ] = max.x; vertices[ 43 ] = min.y; vertices[ 44 ] = min.z;
+	vertices[ 45 ] = max.x; vertices[ 46 ] = max.y; vertices[ 47 ] = min.z;
 
-			this.morphTargetInfluences[ animation.lastFrame ] = 0;
-			this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight;
+	//
 
-			this.morphTargetInfluences[ keyframe ] = 0;
+	vertices[ 48 ] = max.x; vertices[ 49 ] = max.y; vertices[ 50 ] = max.z;
+	vertices[ 51 ] = max.x; vertices[ 52 ] = max.y; vertices[ 53 ] = min.z;
 
-			animation.lastFrame = animation.currentFrame;
-			animation.currentFrame = keyframe;
+	vertices[ 54 ] = min.x; vertices[ 55 ] = max.y; vertices[ 56 ] = max.z;
+	vertices[ 57 ] = min.x; vertices[ 58 ] = max.y; vertices[ 59 ] = min.z;
 
-		}
+	vertices[ 60 ] = min.x; vertices[ 61 ] = min.y; vertices[ 62 ] = max.z;
+	vertices[ 63 ] = min.x; vertices[ 64 ] = min.y; vertices[ 65 ] = min.z;
 
-		var mix = ( animation.time % frameTime ) / frameTime;
+	vertices[ 66 ] = max.x; vertices[ 67 ] = min.y; vertices[ 68 ] = max.z;
+	vertices[ 69 ] = max.x; vertices[ 70 ] = min.y; vertices[ 71 ] = min.z;
 
-		if ( animation.directionBackwards ) mix = 1 - mix;
+	this.geometry.attributes.position.needsUpdate = true;
 
-		this.morphTargetInfluences[ animation.currentFrame ] = mix * weight;
-		this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight;
+	this.geometry.computeBoundingSphere();
 
-	}
+	this.matrix = object.matrixWorld;
+	this.matrixAutoUpdate = false;
 
 };
 
-// File:src/extras/renderers/plugins/LensFlarePlugin.js
+// File:src/extras/helpers/BoundingBoxHelper.js
 
 /**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
  */
 
-THREE.LensFlarePlugin = function ( renderer ) {
-
-	var gl = renderer.context;
-
-	var flares = [];
-
-	var vertexBuffer, elementBuffer;
-	var program, attributes, uniforms;
-	var hasVertexTexture;
-
-	var tempTexture, occlusionTexture;
-
-	var init = function () {
+// a helper to show the world-axis-aligned bounding box for an object
 
-		var vertices = new Float32Array( [
-			-1, -1,  0, 0,
-			 1, -1,  1, 0,
-			 1,  1,  1, 1,
-			-1,  1,  0, 1
-		] );
+THREE.BoundingBoxHelper = function ( object, hex ) {
 
-		var faces = new Uint16Array( [
-			0, 1, 2,
-			0, 2, 3
-		] );
+	var color = ( hex !== undefined ) ? hex : 0x888888;
 
-		// buffers
+	this.object = object;
 
-		vertexBuffer     = gl.createBuffer();
-		elementBuffer    = gl.createBuffer();
+	this.box = new THREE.Box3();
 
-		gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
-		gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
+	THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) );
 
-		gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
-		gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
+};
 
-		// textures
+THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype );
 
-		tempTexture      = gl.createTexture();
-		occlusionTexture = gl.createTexture();
+THREE.BoundingBoxHelper.prototype.update = function () {
 
-		gl.bindTexture( gl.TEXTURE_2D, tempTexture );
-		gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null );
-		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
-		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
-		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
-		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
+	this.box.setFromObject( this.object );
 
-		gl.bindTexture( gl.TEXTURE_2D, occlusionTexture );
-		gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null );
-		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
-		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
-		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
-		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
+	this.box.size( this.scale );
 
-		hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0;
+	this.box.center( this.position );
 
-		if ( hasVertexTexture ) {
+};
 
-			program = createProgram( THREE.ShaderFlares[ "lensFlareVertexTexture" ] );
+// File:src/extras/helpers/CameraHelper.js
 
-		} else {
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ *	- shows frustum, line of sight and up of the camera
+ *	- suitable for fast updates
+ * 	- based on frustum visualization in lightgl.js shadowmap example
+ *		http://evanw.github.com/lightgl.js/tests/shadowmap.html
+ */
 
-			program = createProgram( THREE.ShaderFlares[ "lensFlare" ] );
+THREE.CameraHelper = function ( camera ) {
 
-		}
+	var geometry = new THREE.Geometry();
+	var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } );
 
-		attributes = {
-			vertex: gl.getAttribLocation ( program, "position" ),
-			uv:     gl.getAttribLocation ( program, "uv" )
-		}
+	var pointMap = {};
 
-		uniforms = {
-			renderType:     gl.getUniformLocation( program, "renderType" ),
-			map:            gl.getUniformLocation( program, "map" ),
-			occlusionMap:   gl.getUniformLocation( program, "occlusionMap" ),
-			opacity:        gl.getUniformLocation( program, "opacity" ),
-			color:          gl.getUniformLocation( program, "color" ),
-			scale:          gl.getUniformLocation( program, "scale" ),
-			rotation:       gl.getUniformLocation( program, "rotation" ),
-			screenPosition: gl.getUniformLocation( program, "screenPosition" )
-		};
+	// colors
 
-	};
+	var hexFrustum = 0xffaa00;
+	var hexCone = 0xff0000;
+	var hexUp = 0x00aaff;
+	var hexTarget = 0xffffff;
+	var hexCross = 0x333333;
 
-	this.init = function () {}; // TODO: Remove
+	// near
 
-	/*
-	 * Render lens flares
-	 * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
-	 *         reads these back and calculates occlusion.
-	 */
+	addLine( "n1", "n2", hexFrustum );
+	addLine( "n2", "n4", hexFrustum );
+	addLine( "n4", "n3", hexFrustum );
+	addLine( "n3", "n1", hexFrustum );
 
-	this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
+	// far
 
-		flares.length = 0;
+	addLine( "f1", "f2", hexFrustum );
+	addLine( "f2", "f4", hexFrustum );
+	addLine( "f4", "f3", hexFrustum );
+	addLine( "f3", "f1", hexFrustum );
 
-		scene.traverseVisible( function ( child ) {
+	// sides
 
-			if ( child instanceof THREE.LensFlare ) {
+	addLine( "n1", "f1", hexFrustum );
+	addLine( "n2", "f2", hexFrustum );
+	addLine( "n3", "f3", hexFrustum );
+	addLine( "n4", "f4", hexFrustum );
 
-				flares.push( child );
+	// cone
 
-			}
+	addLine( "p", "n1", hexCone );
+	addLine( "p", "n2", hexCone );
+	addLine( "p", "n3", hexCone );
+	addLine( "p", "n4", hexCone );
 
-		} );
+	// up
 
-		if ( flares.length === 0 ) return;
+	addLine( "u1", "u2", hexUp );
+	addLine( "u2", "u3", hexUp );
+	addLine( "u3", "u1", hexUp );
 
-		var tempPosition = new THREE.Vector3();
+	// target
 
-		var invAspect = viewportHeight / viewportWidth,
-			halfViewportWidth = viewportWidth * 0.5,
-			halfViewportHeight = viewportHeight * 0.5;
+	addLine( "c", "t", hexTarget );
+	addLine( "p", "c", hexCross );
 
-		var size = 16 / viewportHeight,
-			scale = new THREE.Vector2( size * invAspect, size );
+	// cross
 
-		var screenPosition = new THREE.Vector3( 1, 1, 0 ),
-			screenPositionPixels = new THREE.Vector2( 1, 1 );
+	addLine( "cn1", "cn2", hexCross );
+	addLine( "cn3", "cn4", hexCross );
 
-		if ( program === undefined ) {
+	addLine( "cf1", "cf2", hexCross );
+	addLine( "cf3", "cf4", hexCross );
 
-			init();
+	function addLine( a, b, hex ) {
 
-		}
+		addPoint( a, hex );
+		addPoint( b, hex );
 
-		gl.useProgram( program );
+	}
 
-		gl.enableVertexAttribArray( attributes.vertex );
-		gl.enableVertexAttribArray( attributes.uv );
+	function addPoint( id, hex ) {
 
-		// loop through all lens flares to update their occlusion and positions
-		// setup gl and common used attribs/unforms
+		geometry.vertices.push( new THREE.Vector3() );
+		geometry.colors.push( new THREE.Color( hex ) );
 
-		gl.uniform1i( uniforms.occlusionMap, 0 );
-		gl.uniform1i( uniforms.map, 1 );
+		if ( pointMap[ id ] === undefined ) {
 
-		gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
-		gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 );
-		gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
+			pointMap[ id ] = [];
 
-		gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+		}
 
-		gl.disable( gl.CULL_FACE );
-		gl.depthMask( false );
+		pointMap[ id ].push( geometry.vertices.length - 1 );
 
-		for ( var i = 0, l = flares.length; i < l; i ++ ) {
+	}
 
-			size = 16 / viewportHeight;
-			scale.set( size * invAspect, size );
+	THREE.Line.call( this, geometry, material, THREE.LinePieces );
 
-			// calc object screen position
+	this.camera = camera;
+	this.matrix = camera.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-			var flare = flares[ i ];
-			
-			tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] );
+	this.pointMap = pointMap;
 
-			tempPosition.applyMatrix4( camera.matrixWorldInverse );
-			tempPosition.applyProjection( camera.projectionMatrix );
+	this.update();
 
-			// setup arrays for gl programs
+};
 
-			screenPosition.copy( tempPosition )
+THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype );
 
-			screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth;
-			screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight;
+THREE.CameraHelper.prototype.update = function () {
 
-			// screen cull
+	var geometry, pointMap;
+	
+	var vector = new THREE.Vector3();
+	var camera = new THREE.Camera();
 
-			if ( hasVertexTexture || (
-				screenPositionPixels.x > 0 &&
-				screenPositionPixels.x < viewportWidth &&
-				screenPositionPixels.y > 0 &&
-				screenPositionPixels.y < viewportHeight ) ) {
+	var setPoint = function ( point, x, y, z ) {
 
-				// save current RGB to temp texture
+		vector.set( x, y, z ).unproject( camera );
 
-				gl.activeTexture( gl.TEXTURE1 );
-				gl.bindTexture( gl.TEXTURE_2D, tempTexture );
-				gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
+		var points = pointMap[ point ];
 
+		if ( points !== undefined ) {
 
-				// render pink quad
+			for ( var i = 0, il = points.length; i < il; i ++ ) {
 
-				gl.uniform1i( uniforms.renderType, 0 );
-				gl.uniform2f( uniforms.scale, scale.x, scale.y );
-				gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
+				geometry.vertices[ points[ i ] ].copy( vector );
 
-				gl.disable( gl.BLEND );
-				gl.enable( gl.DEPTH_TEST );
+			}
 
-				gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
+		}
 
+	};
 
-				// copy result to occlusionMap
+	return function () {
 
-				gl.activeTexture( gl.TEXTURE0 );
-				gl.bindTexture( gl.TEXTURE_2D, occlusionTexture );
-				gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
+		geometry = this.geometry;
+		pointMap = this.pointMap;
 
+		var w = 1, h = 1;
 
-				// restore graphics
+		// we need just camera projection matrix
+		// world matrix must be identity
 
-				gl.uniform1i( uniforms.renderType, 1 );
-				gl.disable( gl.DEPTH_TEST );
+		camera.projectionMatrix.copy( this.camera.projectionMatrix );
 
-				gl.activeTexture( gl.TEXTURE1 );
-				gl.bindTexture( gl.TEXTURE_2D, tempTexture );
-				gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
+		// center / target
 
+		setPoint( "c", 0, 0, - 1 );
+		setPoint( "t", 0, 0,  1 );
 
-				// update object positions
+		// near
 
-				flare.positionScreen.copy( screenPosition )
+		setPoint( "n1", - w, - h, - 1 );
+		setPoint( "n2",   w, - h, - 1 );
+		setPoint( "n3", - w,   h, - 1 );
+		setPoint( "n4",   w,   h, - 1 );
 
-				if ( flare.customUpdateCallback ) {
+		// far
 
-					flare.customUpdateCallback( flare );
+		setPoint( "f1", - w, - h, 1 );
+		setPoint( "f2",   w, - h, 1 );
+		setPoint( "f3", - w,   h, 1 );
+		setPoint( "f4",   w,   h, 1 );
 
-				} else {
+		// up
 
-					flare.updateLensFlares();
+		setPoint( "u1",   w * 0.7, h * 1.1, - 1 );
+		setPoint( "u2", - w * 0.7, h * 1.1, - 1 );
+		setPoint( "u3",         0, h * 2,   - 1 );
 
-				}
+		// cross
 
-				// render flares
+		setPoint( "cf1", - w,   0, 1 );
+		setPoint( "cf2",   w,   0, 1 );
+		setPoint( "cf3",   0, - h, 1 );
+		setPoint( "cf4",   0,   h, 1 );
 
-				gl.uniform1i( uniforms.renderType, 2 );
-				gl.enable( gl.BLEND );
+		setPoint( "cn1", - w,   0, - 1 );
+		setPoint( "cn2",   w,   0, - 1 );
+		setPoint( "cn3",   0, - h, - 1 );
+		setPoint( "cn4",   0,   h, - 1 );
 
-				for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {
+		geometry.verticesNeedUpdate = true;
 
-					var sprite = flare.lensFlares[ j ];
+	};
 
-					if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) {
+}();
 
-						screenPosition.x = sprite.x;
-						screenPosition.y = sprite.y;
-						screenPosition.z = sprite.z;
+// File:src/extras/helpers/DirectionalLightHelper.js
 
-						size = sprite.size * sprite.scale / viewportHeight;
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
 
-						scale.x = size * invAspect;
-						scale.y = size;
+THREE.DirectionalLightHelper = function ( light, size ) {
 
-						gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
-						gl.uniform2f( uniforms.scale, scale.x, scale.y );
-						gl.uniform1f( uniforms.rotation, sprite.rotation );
+	THREE.Object3D.call( this );
 
-						gl.uniform1f( uniforms.opacity, sprite.opacity );
-						gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );
+	this.light = light;
+	this.light.updateMatrixWorld();
 
-						renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );
-						renderer.setTexture( sprite.texture, 1 );
+	this.matrix = light.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-						gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
+	size = size || 1;
 
-					}
+	var geometry = new THREE.Geometry();
+	geometry.vertices.push(
+		new THREE.Vector3( - size,   size, 0 ),
+		new THREE.Vector3(   size,   size, 0 ),
+		new THREE.Vector3(   size, - size, 0 ),
+		new THREE.Vector3( - size, - size, 0 ),
+		new THREE.Vector3( - size,   size, 0 )
+	);
 
-				}
+	var material = new THREE.LineBasicMaterial( { fog: false } );
+	material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
 
-			}
+	this.lightPlane = new THREE.Line( geometry, material );
+	this.add( this.lightPlane );
 
-		}
+	geometry = new THREE.Geometry();
+	geometry.vertices.push(
+		new THREE.Vector3(),
+		new THREE.Vector3()
+	);
 
-		// restore gl
+	material = new THREE.LineBasicMaterial( { fog: false } );
+	material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
 
-		gl.enable( gl.CULL_FACE );
-		gl.enable( gl.DEPTH_TEST );
-		gl.depthMask( true );
+	this.targetLine = new THREE.Line( geometry, material );
+	this.add( this.targetLine );
 
-		renderer.resetGLState();
+	this.update();
 
-	};
+};
 
-	function createProgram ( shader ) {
+THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype );
 
-		var program = gl.createProgram();
+THREE.DirectionalLightHelper.prototype.dispose = function () {
 
-		var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
-		var vertexShader = gl.createShader( gl.VERTEX_SHADER );
+	this.lightPlane.geometry.dispose();
+	this.lightPlane.material.dispose();
+	this.targetLine.geometry.dispose();
+	this.targetLine.material.dispose();
+};
 
-		var prefix = "precision " + renderer.getPrecision() + " float;\n";
+THREE.DirectionalLightHelper.prototype.update = function () {
 
-		gl.shaderSource( fragmentShader, prefix + shader.fragmentShader );
-		gl.shaderSource( vertexShader, prefix + shader.vertexShader );
+	var v1 = new THREE.Vector3();
+	var v2 = new THREE.Vector3();
+	var v3 = new THREE.Vector3();
 
-		gl.compileShader( fragmentShader );
-		gl.compileShader( vertexShader );
+	return function () {
 
-		gl.attachShader( program, fragmentShader );
-		gl.attachShader( program, vertexShader );
+		v1.setFromMatrixPosition( this.light.matrixWorld );
+		v2.setFromMatrixPosition( this.light.target.matrixWorld );
+		v3.subVectors( v2, v1 );
 
-		gl.linkProgram( program );
+		this.lightPlane.lookAt( v3 );
+		this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
 
-		return program;
+		this.targetLine.geometry.vertices[ 1 ].copy( v3 );
+		this.targetLine.geometry.verticesNeedUpdate = true;
+		this.targetLine.material.color.copy( this.lightPlane.material.color );
 
 	};
 
-};
+}();
 
-// File:src/extras/renderers/plugins/ShadowMapPlugin.js
+// File:src/extras/helpers/EdgesHelper.js
 
 /**
- * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
  */
 
-THREE.ShadowMapPlugin = function () {
+THREE.EdgesHelper = function ( object, hex ) {
 
-	var _gl,
-	_renderer,
-	_lights, _webglObjects, _webglObjectsImmediate,
-	_depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
+	var color = ( hex !== undefined ) ? hex : 0xffffff;
 
-	_frustum = new THREE.Frustum(),
-	_projScreenMatrix = new THREE.Matrix4(),
+	var edge = [ 0, 0 ], hash = {};
+	var sortFunction = function ( a, b ) { return a - b };
 
-	_min = new THREE.Vector3(),
-	_max = new THREE.Vector3(),
+	var keys = [ 'a', 'b', 'c' ];
+	var geometry = new THREE.BufferGeometry();
 
-	_matrixPosition = new THREE.Vector3(),
-	
-	_renderList = [];
+	var geometry2 = object.geometry.clone();
 
-	this.init = function ( renderer, lights, webglObjects, webglObjectsImmediate ) {
+	geometry2.mergeVertices();
+	geometry2.computeFaceNormals();
 
-		_gl = renderer.context;
-		_renderer = renderer;
-		_lights = lights;
+	var vertices = geometry2.vertices;
+	var faces = geometry2.faces;
+	var numEdges = 0;
 
-		_webglObjects = webglObjects;
-		_webglObjectsImmediate = webglObjectsImmediate;
+	for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-		var depthShader = THREE.ShaderLib[ "depthRGBA" ];
-		var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
+		var face = faces[ i ];
 
-		_depthMaterial = new THREE.ShaderMaterial( {
-			uniforms: depthUniforms,
-			vertexShader: depthShader.vertexShader,
-			fragmentShader: depthShader.fragmentShader
-		 } );
+		for ( var j = 0; j < 3; j ++ ) {
 
-		_depthMaterialMorph = new THREE.ShaderMaterial( {
-			uniforms: depthUniforms,
-			vertexShader: depthShader.vertexShader,
-			fragmentShader: depthShader.fragmentShader,
-			morphTargets: true
-		} );
+			edge[ 0 ] = face[ keys[ j ] ];
+			edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
+			edge.sort( sortFunction );
 
-		_depthMaterialSkin = new THREE.ShaderMaterial( {
-			uniforms: depthUniforms,
-			vertexShader: depthShader.vertexShader,
-			fragmentShader: depthShader.fragmentShader,
-			skinning: true
-		} );
+			var key = edge.toString();
 
-		_depthMaterialMorphSkin = new THREE.ShaderMaterial( {
-			uniforms: depthUniforms,
-			vertexShader: depthShader.vertexShader,
-			fragmentShader: depthShader.fragmentShader,
-			morphTargets: true,
-			skinning: true
-		} );
+			if ( hash[ key ] === undefined ) {
 
-		_depthMaterial._shadowPass = true;
-		_depthMaterialMorph._shadowPass = true;
-		_depthMaterialSkin._shadowPass = true;
-		_depthMaterialMorphSkin._shadowPass = true;
+				hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined };
+				numEdges ++;
 
-	};
+			} else {
 
-	this.render = function ( scene, camera ) {
+				hash[ key ].face2 = i;
 
-		if ( _renderer.shadowMapEnabled === false || _renderer.shadowMapAutoUpdate === false ) return;
+			}
 
-		this.update( scene, camera );
+		}
 
-	};
+	}
 
-	this.update = function ( scene, camera ) {
+	geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( numEdges * 2 * 3 ), 3 ) );
 
-		var i, il, j, jl, n,
+	var coords = geometry.attributes.position.array;
 
-		shadowMap, shadowMatrix, shadowCamera,
-		program, buffer, material,
-		webglObject, object, light,
+	var index = 0;
 
-		lights = [],
-		k = 0,
+	for ( var key in hash ) {
 
-		fog = null;
+		var h = hash[ key ];
 
-		// set GL state for depth map
+		if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) < 0.9999 ) { // hardwired const OK
 
-		_gl.clearColor( 1, 1, 1, 1 );
-		_gl.disable( _gl.BLEND );
+			var vertex = vertices[ h.vert1 ];
+			coords[ index ++ ] = vertex.x;
+			coords[ index ++ ] = vertex.y;
+			coords[ index ++ ] = vertex.z;
 
-		_gl.enable( _gl.CULL_FACE );
-		_gl.frontFace( _gl.CCW );
+			vertex = vertices[ h.vert2 ];
+			coords[ index ++ ] = vertex.x;
+			coords[ index ++ ] = vertex.y;
+			coords[ index ++ ] = vertex.z;
 
-		if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
+		}
 
-			_gl.cullFace( _gl.FRONT );
+	}
 
-		} else {
+	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces );
 
-			_gl.cullFace( _gl.BACK );
+	this.matrix = object.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-		}
+};
 
-		_renderer.setDepthTest( true );
+THREE.EdgesHelper.prototype = Object.create( THREE.Line.prototype );
 
-		// preprocess lights
-		// 	- skip lights that are not casting shadows
-		//	- create virtual lights for cascaded shadow maps
+// File:src/extras/helpers/FaceNormalsHelper.js
 
-		for ( i = 0, il = _lights.length; i < il; i ++ ) {
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
 
-			light = _lights[ i ];
+THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) {
 
-			if ( ! light.castShadow ) continue;
+	this.object = object;
 
-			if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) {
+	this.size = ( size !== undefined ) ? size : 1;
 
-				for ( n = 0; n < light.shadowCascadeCount; n ++ ) {
+	var color = ( hex !== undefined ) ? hex : 0xffff00;
 
-					var virtualLight;
+	var width = ( linewidth !== undefined ) ? linewidth : 1;
 
-					if ( ! light.shadowCascadeArray[ n ] ) {
+	var geometry = new THREE.Geometry();
 
-						virtualLight = createVirtualLight( light, n );
-						virtualLight.originalCamera = camera;
+	var faces = this.object.geometry.faces;
 
-						var gyro = new THREE.Gyroscope();
-						gyro.position.copy( light.shadowCascadeOffset );
+	for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-						gyro.add( virtualLight );
-						gyro.add( virtualLight.target );
+		geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() );
 
-						camera.add( gyro );
+	}
 
-						light.shadowCascadeArray[ n ] = virtualLight;
+	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
 
-						console.log( "Created virtualLight", virtualLight );
+	this.matrixAutoUpdate = false;
 
-					} else {
+	this.normalMatrix = new THREE.Matrix3();
 
-						virtualLight = light.shadowCascadeArray[ n ];
+	this.update();
 
-					}
+};
 
-					updateVirtualLight( light, n );
+THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype );
 
-					lights[ k ] = virtualLight;
-					k ++;
+THREE.FaceNormalsHelper.prototype.update = function () {
 
-				}
+	var vertices = this.geometry.vertices;
 
-			} else {
+	var object = this.object;
+	var objectVertices = object.geometry.vertices;
+	var objectFaces = object.geometry.faces;
+	var objectWorldMatrix = object.matrixWorld;
 
-				lights[ k ] = light;
-				k ++;
+	object.updateMatrixWorld( true );
 
-			}
+	this.normalMatrix.getNormalMatrix( objectWorldMatrix );
 
-		}
+	for ( var i = 0, i2 = 0, l = objectFaces.length; i < l; i ++, i2 += 2 ) {
 
-		// render depth map
+		var face = objectFaces[ i ];
 
-		for ( i = 0, il = lights.length; i < il; i ++ ) {
+		vertices[ i2 ].copy( objectVertices[ face.a ] )
+			.add( objectVertices[ face.b ] )
+			.add( objectVertices[ face.c ] )
+			.divideScalar( 3 )
+			.applyMatrix4( objectWorldMatrix );
 
-			light = lights[ i ];
+		vertices[ i2 + 1 ].copy( face.normal )
+			.applyMatrix3( this.normalMatrix )
+			.normalize()
+			.multiplyScalar( this.size )
+			.add( vertices[ i2 ] );
 
-			if ( ! light.shadowMap ) {
+	}
 
-				var shadowFilter = THREE.LinearFilter;
+	this.geometry.verticesNeedUpdate = true;
 
-				if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) {
+	return this;
 
-					shadowFilter = THREE.NearestFilter;
+};
 
-				}
 
-				var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat };
+// File:src/extras/helpers/GridHelper.js
 
-				light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars );
-				light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-				light.shadowMatrix = new THREE.Matrix4();
+THREE.GridHelper = function ( size, step ) {
 
-			}
+	var geometry = new THREE.Geometry();
+	var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
 
-			if ( ! light.shadowCamera ) {
+	this.color1 = new THREE.Color( 0x444444 );
+	this.color2 = new THREE.Color( 0x888888 );
 
-				if ( light instanceof THREE.SpotLight ) {
+	for ( var i = - size; i <= size; i += step ) {
 
-					light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar );
+		geometry.vertices.push(
+			new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ),
+			new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size )
+		);
 
-				} else if ( light instanceof THREE.DirectionalLight ) {
+		var color = i === 0 ? this.color1 : this.color2;
 
-					light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar );
+		geometry.colors.push( color, color, color, color );
 
-				} else {
+	}
 
-					console.error( "Unsupported light type for shadow" );
-					continue;
+	THREE.Line.call( this, geometry, material, THREE.LinePieces );
 
-				}
+};
 
-				scene.add( light.shadowCamera );
+THREE.GridHelper.prototype = Object.create( THREE.Line.prototype );
 
-				if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) {
 
-			}
+	this.color1.set( colorCenterLine );
+	this.color2.set( colorGrid );
 
-			if ( light.shadowCameraVisible && ! light.cameraHelper ) {
+	this.geometry.colorsNeedUpdate = true;
 
-				light.cameraHelper = new THREE.CameraHelper( light.shadowCamera );
-				scene.add( light.cameraHelper );
+}
 
-			}
+// File:src/extras/helpers/HemisphereLightHelper.js
 
-			if ( light.isVirtual && virtualLight.originalCamera == camera ) {
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-				updateShadowCamera( camera, light );
+THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) {
 
-			}
+	THREE.Object3D.call( this );
 
-			shadowMap = light.shadowMap;
-			shadowMatrix = light.shadowMatrix;
-			shadowCamera = light.shadowCamera;
+	this.light = light;
+	this.light.updateMatrixWorld();
 
-			//
+	this.matrix = light.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-			shadowCamera.position.setFromMatrixPosition( light.matrixWorld );
-			_matrixPosition.setFromMatrixPosition( light.target.matrixWorld );
-			shadowCamera.lookAt( _matrixPosition );
-			shadowCamera.updateMatrixWorld();
+	this.colors = [ new THREE.Color(), new THREE.Color() ];
 
-			shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
+	var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
+	geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
 
-			//
+	for ( var i = 0, il = 8; i < il; i ++ ) {
 
-			if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible;
-			if ( light.shadowCameraVisible ) light.cameraHelper.update();
+		geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ];
 
-			// compute shadow matrix
+	}
 
-			shadowMatrix.set(
-				0.5, 0.0, 0.0, 0.5,
-				0.0, 0.5, 0.0, 0.5,
-				0.0, 0.0, 0.5, 0.5,
-				0.0, 0.0, 0.0, 1.0
-			);
+	var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } );
 
-			shadowMatrix.multiply( shadowCamera.projectionMatrix );
-			shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
+	this.lightSphere = new THREE.Mesh( geometry, material );
+	this.add( this.lightSphere );
 
-			// update camera matrices and frustum
+	this.update();
 
-			_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
-			_frustum.setFromMatrix( _projScreenMatrix );
+};
 
-			// render shadow map
+THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype );
 
-			_renderer.setRenderTarget( shadowMap );
-			_renderer.clear();
+THREE.HemisphereLightHelper.prototype.dispose = function () {
+	this.lightSphere.geometry.dispose();
+	this.lightSphere.material.dispose();
+};
 
-			// set object matrices & frustum culling
+THREE.HemisphereLightHelper.prototype.update = function () {
 
-			_renderList.length = 0;
+	var vector = new THREE.Vector3();
 
-			projectObject( scene, scene, shadowCamera );
+	return function () {
 
+		this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity );
+		this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity );
 
-			// render regular objects
+		this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() );
+		this.lightSphere.geometry.colorsNeedUpdate = true;
 
-			var objectMaterial, useMorphing, useSkinning;
+	}
 
-			for ( j = 0, jl = _renderList.length; j < jl; j ++ ) {
+}();
 
-				webglObject = _renderList[ j ];
+// File:src/extras/helpers/PointLightHelper.js
 
-				object = webglObject.object;
-				buffer = webglObject.buffer;
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-				// culling is overriden globally for all objects
-				// while rendering depth map
+THREE.PointLightHelper = function ( light, sphereSize ) {
 
-				// need to deal with MeshFaceMaterial somehow
-				// in that case just use the first of material.materials for now
-				// (proper solution would require to break objects by materials
-				//  similarly to regular rendering and then set corresponding
-				//  depth materials per each chunk instead of just once per object)
+	this.light = light;
+	this.light.updateMatrixWorld();
 
-				objectMaterial = getObjectMaterial( object );
+	var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
+	var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
+	material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
 
-				useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
-				useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning;
+	THREE.Mesh.call( this, geometry, material );
 
-				if ( object.customDepthMaterial ) {
+	this.matrix = this.light.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-					material = object.customDepthMaterial;
+	/*
+	var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );
+	var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );
 
-				} else if ( useSkinning ) {
+	this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
+	this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );
 
-					material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;
+	var d = light.distance;
 
-				} else if ( useMorphing ) {
+	if ( d === 0.0 ) {
 
-					material = _depthMaterialMorph;
+		this.lightDistance.visible = false;
 
-				} else {
+	} else {
 
-					material = _depthMaterial;
+		this.lightDistance.scale.set( d, d, d );
 
-				}
+	}
 
-				_renderer.setMaterialFaces( objectMaterial );
+	this.add( this.lightDistance );
+	*/
 
-				if ( buffer instanceof THREE.BufferGeometry ) {
+};
 
-					_renderer.renderBufferDirect( shadowCamera, _lights, fog, material, buffer, object );
+THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype );
 
-				} else {
+THREE.PointLightHelper.prototype.dispose = function () {
 
-					_renderer.renderBuffer( shadowCamera, _lights, fog, material, buffer, object );
+	this.geometry.dispose();
+	this.material.dispose();
+};
 
-				}
+THREE.PointLightHelper.prototype.update = function () {
 
-			}
+	this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
 
-			// set matrices and render immediate objects
+	/*
+	var d = this.light.distance;
 
-			for ( j = 0, jl = _webglObjectsImmediate.length; j < jl; j ++ ) {
+	if ( d === 0.0 ) {
 
-				webglObject = _webglObjectsImmediate[ j ];
-				object = webglObject.object;
+		this.lightDistance.visible = false;
 
-				if ( object.visible && object.castShadow ) {
+	} else {
 
-					object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
+		this.lightDistance.visible = true;
+		this.lightDistance.scale.set( d, d, d );
 
-					_renderer.renderImmediateObject( shadowCamera, _lights, fog, _depthMaterial, object );
+	}
+	*/
 
-				}
+};
 
-			}
+// File:src/extras/helpers/SkeletonHelper.js
 
-		}
+/**
+ * @author Sean Griffin / http://twitter.com/sgrif
+ * @author Michael Guerrero / http://realitymeltdown.com
+ * @author mrdoob / http://mrdoob.com/
+ * @author ikerr / http://verold.com
+ */
 
-		// restore GL state
+THREE.SkeletonHelper = function ( object ) {
 
-		var clearColor = _renderer.getClearColor(),
-		clearAlpha = _renderer.getClearAlpha();
+	this.bones = this.getBoneList( object );
 
-		_gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
-		_gl.enable( _gl.BLEND );
+	var geometry = new THREE.Geometry();
 
-		if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
+	for ( var i = 0; i < this.bones.length; i ++ ) {
 
-			_gl.cullFace( _gl.BACK );
+		var bone = this.bones[ i ];
 
-		}
+		if ( bone.parent instanceof THREE.Bone ) {
 
-		_renderer.resetGLState();
+			geometry.vertices.push( new THREE.Vector3() );
+			geometry.vertices.push( new THREE.Vector3() );
+			geometry.colors.push( new THREE.Color( 0, 0, 1 ) );
+			geometry.colors.push( new THREE.Color( 0, 1, 0 ) );
 
-	};
+		}
 
-	function projectObject( scene, object, shadowCamera ){
+	}
 
-		if ( object.visible ) {
+	var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } );
 
-			var webglObjects = _webglObjects[ object.id ];
+	THREE.Line.call( this, geometry, material, THREE.LinePieces );
 
-			if ( webglObjects && object.castShadow && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) {
+	this.root = object;
 
-				for (var i = 0, l = webglObjects.length; i < l; i ++ ) {
+	this.matrix = object.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-					var webglObject = webglObjects[ i ];
+	this.update();
 
-					object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
-					_renderList.push( webglObject );
+};
 
-				}
 
-			}
+THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype );
 
-			for ( var i = 0, l = object.children.length; i < l; i ++ ) {
+THREE.SkeletonHelper.prototype.getBoneList = function( object ) {
 
-				projectObject( scene, object.children[ i ], shadowCamera );
+	var boneList = [];
 
-			}
+	if ( object instanceof THREE.Bone ) {
 
-		}
+		boneList.push( object );
 
 	}
 
-	function createVirtualLight( light, cascade ) {
-
-		var virtualLight = new THREE.DirectionalLight();
+	for ( var i = 0; i < object.children.length; i ++ ) {
 
-		virtualLight.isVirtual = true;
+		boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) );
 
-		virtualLight.onlyShadow = true;
-		virtualLight.castShadow = true;
+	}
 
-		virtualLight.shadowCameraNear = light.shadowCameraNear;
-		virtualLight.shadowCameraFar = light.shadowCameraFar;
+	return boneList;
 
-		virtualLight.shadowCameraLeft = light.shadowCameraLeft;
-		virtualLight.shadowCameraRight = light.shadowCameraRight;
-		virtualLight.shadowCameraBottom = light.shadowCameraBottom;
-		virtualLight.shadowCameraTop = light.shadowCameraTop;
+};
 
-		virtualLight.shadowCameraVisible = light.shadowCameraVisible;
+THREE.SkeletonHelper.prototype.update = function () {
 
-		virtualLight.shadowDarkness = light.shadowDarkness;
+	var geometry = this.geometry;
 
-		virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
-		virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ];
-		virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ];
+	var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld );
 
-		virtualLight.pointsWorld = [];
-		virtualLight.pointsFrustum = [];
+	var boneMatrix = new THREE.Matrix4();
 
-		var pointsWorld = virtualLight.pointsWorld,
-			pointsFrustum = virtualLight.pointsFrustum;
+	var j = 0;
 
-		for ( var i = 0; i < 8; i ++ ) {
+	for ( var i = 0; i < this.bones.length; i ++ ) {
 
-			pointsWorld[ i ] = new THREE.Vector3();
-			pointsFrustum[ i ] = new THREE.Vector3();
+		var bone = this.bones[ i ];
 
-		}
+		if ( bone.parent instanceof THREE.Bone ) {
 
-		var nearZ = light.shadowCascadeNearZ[ cascade ];
-		var farZ = light.shadowCascadeFarZ[ cascade ];
+			boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld );
+			geometry.vertices[ j ].setFromMatrixPosition( boneMatrix );
 
-		pointsFrustum[ 0 ].set( - 1, - 1, nearZ );
-		pointsFrustum[ 1 ].set(  1, - 1, nearZ );
-		pointsFrustum[ 2 ].set( - 1,  1, nearZ );
-		pointsFrustum[ 3 ].set(  1,  1, nearZ );
+			boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld );
+			geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix );
 
-		pointsFrustum[ 4 ].set( - 1, - 1, farZ );
-		pointsFrustum[ 5 ].set(  1, - 1, farZ );
-		pointsFrustum[ 6 ].set( - 1,  1, farZ );
-		pointsFrustum[ 7 ].set(  1,  1, farZ );
+			j += 2;
 
-		return virtualLight;
+		}
 
 	}
 
-	// Synchronize virtual light with the original light
+	geometry.verticesNeedUpdate = true;
 
-	function updateVirtualLight( light, cascade ) {
+	geometry.computeBoundingSphere();
 
-		var virtualLight = light.shadowCascadeArray[ cascade ];
+};
 
-		virtualLight.position.copy( light.position );
-		virtualLight.target.position.copy( light.target.position );
-		virtualLight.lookAt( virtualLight.target );
+// File:src/extras/helpers/SpotLightHelper.js
 
-		virtualLight.shadowCameraVisible = light.shadowCameraVisible;
-		virtualLight.shadowDarkness = light.shadowDarkness;
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
 
-		virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
+THREE.SpotLightHelper = function ( light ) {
 
-		var nearZ = light.shadowCascadeNearZ[ cascade ];
-		var farZ = light.shadowCascadeFarZ[ cascade ];
+	THREE.Object3D.call( this );
 
-		var pointsFrustum = virtualLight.pointsFrustum;
+	this.light = light;
+	this.light.updateMatrixWorld();
 
-		pointsFrustum[ 0 ].z = nearZ;
-		pointsFrustum[ 1 ].z = nearZ;
-		pointsFrustum[ 2 ].z = nearZ;
-		pointsFrustum[ 3 ].z = nearZ;
+	this.matrix = light.matrixWorld;
+	this.matrixAutoUpdate = false;
 
-		pointsFrustum[ 4 ].z = farZ;
-		pointsFrustum[ 5 ].z = farZ;
-		pointsFrustum[ 6 ].z = farZ;
-		pointsFrustum[ 7 ].z = farZ;
+	var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true );
+
+	geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) );
+	geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
 
-	}
+	var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
 
-	// Fit shadow camera's ortho frustum to camera frustum
+	this.cone = new THREE.Mesh( geometry, material );
+	this.add( this.cone );
 
-	function updateShadowCamera( camera, light ) {
+	this.update();
 
-		var shadowCamera = light.shadowCamera,
-			pointsFrustum = light.pointsFrustum,
-			pointsWorld = light.pointsWorld;
+};
 
-		_min.set( Infinity, Infinity, Infinity );
-		_max.set( - Infinity, - Infinity, - Infinity );
+THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype );
 
-		for ( var i = 0; i < 8; i ++ ) {
+THREE.SpotLightHelper.prototype.dispose = function () {
+	this.cone.geometry.dispose();
+	this.cone.material.dispose();
+};
 
-			var p = pointsWorld[ i ];
+THREE.SpotLightHelper.prototype.update = function () {
 
-			p.copy( pointsFrustum[ i ] );
-			p.unproject( camera );
+	var vector = new THREE.Vector3();
+	var vector2 = new THREE.Vector3();
 
-			p.applyMatrix4( shadowCamera.matrixWorldInverse );
+	return function () {
 
-			if ( p.x < _min.x ) _min.x = p.x;
-			if ( p.x > _max.x ) _max.x = p.x;
+		var coneLength = this.light.distance ? this.light.distance : 10000;
+		var coneWidth = coneLength * Math.tan( this.light.angle );
 
-			if ( p.y < _min.y ) _min.y = p.y;
-			if ( p.y > _max.y ) _max.y = p.y;
+		this.cone.scale.set( coneWidth, coneWidth, coneLength );
 
-			if ( p.z < _min.z ) _min.z = p.z;
-			if ( p.z > _max.z ) _max.z = p.z;
+		vector.setFromMatrixPosition( this.light.matrixWorld );
+		vector2.setFromMatrixPosition( this.light.target.matrixWorld );
 
-		}
+		this.cone.lookAt( vector2.sub( vector ) );
 
-		shadowCamera.left = _min.x;
-		shadowCamera.right = _max.x;
-		shadowCamera.top = _max.y;
-		shadowCamera.bottom = _min.y;
+		this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
 
-		// can't really fit near/far
-		//shadowCamera.near = _min.z;
-		//shadowCamera.far = _max.z;
+	};
 
-		shadowCamera.updateProjectionMatrix();
+}();
 
-	}
+// File:src/extras/helpers/VertexNormalsHelper.js
 
-	// For the moment just ignore objects that have multiple materials with different animation methods
-	// Only the first material will be taken into account for deciding which depth material to use for shadow maps
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
 
-	function getObjectMaterial( object ) {
+THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) {
 
-		return object.material instanceof THREE.MeshFaceMaterial
-			? object.material.materials[ 0 ]
-			: object.material;
+	this.object = object;
 
-	};
+	this.size = ( size !== undefined ) ? size : 1;
 
-};
+	var color = ( hex !== undefined ) ? hex : 0xff0000;
 
-// File:src/extras/renderers/plugins/SpritePlugin.js
+	var width = ( linewidth !== undefined ) ? linewidth : 1;
 
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- */
+	var geometry = new THREE.Geometry();
 
-THREE.SpritePlugin = function ( renderer ) {
+	var vertices = object.geometry.vertices;
 
-	var gl = renderer.context;
+	var faces = object.geometry.faces;
 
-	var sprites = [];
+	for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-	var vertexBuffer, elementBuffer;
-	var program, attributes, uniforms;
+		var face = faces[ i ];
 
-	var texture;
-	
-	var init = function () {
+		for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
 
-		var vertices = new Float32Array( [
-			- 0.5, - 0.5,  0, 0,
-			  0.5, - 0.5,  1, 0,
-			  0.5,   0.5,  1, 1,
-			- 0.5,   0.5,  0, 1
-		] );
+			geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() );
 
-		var faces = new Uint16Array( [
-			0, 1, 2,
-			0, 2, 3
-		] );
+		}
 
-		vertexBuffer  = gl.createBuffer();
-		elementBuffer = gl.createBuffer();
+	}
 
-		gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
-		gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
+	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
 
-		gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
-		gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
+	this.matrixAutoUpdate = false;
 
-		program = createProgram();
+	this.normalMatrix = new THREE.Matrix3();
 
-		attributes = {
-			position:			gl.getAttribLocation ( program, 'position' ),
-			uv:					gl.getAttribLocation ( program, 'uv' )
-		};
+	this.update();
 
-		uniforms = {
-			uvOffset:			gl.getUniformLocation( program, 'uvOffset' ),
-			uvScale:			gl.getUniformLocation( program, 'uvScale' ),
+};
 
-			rotation:			gl.getUniformLocation( program, 'rotation' ),
-			scale:				gl.getUniformLocation( program, 'scale' ),
+THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype );
 
-			color:				gl.getUniformLocation( program, 'color' ),
-			map:				gl.getUniformLocation( program, 'map' ),
-			opacity:			gl.getUniformLocation( program, 'opacity' ),
+THREE.VertexNormalsHelper.prototype.update = ( function ( object ) {
 
-			modelViewMatrix: 	gl.getUniformLocation( program, 'modelViewMatrix' ),
-			projectionMatrix:	gl.getUniformLocation( program, 'projectionMatrix' ),
+	var v1 = new THREE.Vector3();
 
-			fogType:			gl.getUniformLocation( program, 'fogType' ),
-			fogDensity:			gl.getUniformLocation( program, 'fogDensity' ),
-			fogNear:			gl.getUniformLocation( program, 'fogNear' ),
-			fogFar:				gl.getUniformLocation( program, 'fogFar' ),
-			fogColor:			gl.getUniformLocation( program, 'fogColor' ),
+	return function( object ) {
 
-			alphaTest:			gl.getUniformLocation( program, 'alphaTest' )
-		};
+		var keys = [ 'a', 'b', 'c', 'd' ];
 
-		var canvas = document.createElement( 'canvas' );
-		canvas.width = 8;
-		canvas.height = 8;
+		this.object.updateMatrixWorld( true );
 
-		var context = canvas.getContext( '2d' );
-		context.fillStyle = 'white';
-		context.fillRect( 0, 0, 8, 8 );
+		this.normalMatrix.getNormalMatrix( this.object.matrixWorld );
 
-		texture = new THREE.Texture( canvas );
-		texture.needsUpdate = true;
+		var vertices = this.geometry.vertices;
 
-	};
+		var verts = this.object.geometry.vertices;
 
-	this.init = function () {}; // TODO: Remove
+		var faces = this.object.geometry.faces;
 
-	this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
+		var worldMatrix = this.object.matrixWorld;
 
-		sprites.length = 0;
+		var idx = 0;
 
-		scene.traverseVisible( function ( child ) {
+		for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-			if ( child instanceof THREE.Sprite ) {
+			var face = faces[ i ];
 
-				sprites.push( child );
+			for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
 
-			}
+				var vertexId = face[ keys[ j ] ];
+				var vertex = verts[ vertexId ];
 
-		} );
+				var normal = face.vertexNormals[ j ];
 
-		if ( sprites.length === 0 ) return;
+				vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix );
 
-		// setup gl
+				v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size );
 
-		if ( program === undefined ) {
+				v1.add( vertices[ idx ] );
+				idx = idx + 1;
 
-			init();
+				vertices[ idx ].copy( v1 );
+				idx = idx + 1;
 
-		}
+			}
 
-		gl.useProgram( program );
+		}
 
-		gl.enableVertexAttribArray( attributes.position );
-		gl.enableVertexAttribArray( attributes.uv );
+		this.geometry.verticesNeedUpdate = true;
 
-		gl.disable( gl.CULL_FACE );
-		gl.enable( gl.BLEND );
+		return this;
 
-		gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
-		gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 );
-		gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
+	}
 
-		gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
+}());
 
-		gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
+// File:src/extras/helpers/VertexTangentsHelper.js
 
-		gl.activeTexture( gl.TEXTURE0 );
-		gl.uniform1i( uniforms.map, 0 );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
 
-		var oldFogType = 0;
-		var sceneFogType = 0;
-		var fog = scene.fog;
+THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) {
 
-		if ( fog ) {
+	this.object = object;
 
-			gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );
+	this.size = ( size !== undefined ) ? size : 1;
 
-			if ( fog instanceof THREE.Fog ) {
+	var color = ( hex !== undefined ) ? hex : 0x0000ff;
 
-				gl.uniform1f( uniforms.fogNear, fog.near );
-				gl.uniform1f( uniforms.fogFar, fog.far );
+	var width = ( linewidth !== undefined ) ? linewidth : 1;
 
-				gl.uniform1i( uniforms.fogType, 1 );
-				oldFogType = 1;
-				sceneFogType = 1;
+	var geometry = new THREE.Geometry();
 
-			} else if ( fog instanceof THREE.FogExp2 ) {
+	var vertices = object.geometry.vertices;
 
-				gl.uniform1f( uniforms.fogDensity, fog.density );
+	var faces = object.geometry.faces;
 
-				gl.uniform1i( uniforms.fogType, 2 );
-				oldFogType = 2;
-				sceneFogType = 2;
+	for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-			}
+		var face = faces[ i ];
 
-		} else {
+		for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) {
 
-			gl.uniform1i( uniforms.fogType, 0 );
-			oldFogType = 0;
-			sceneFogType = 0;
+			geometry.vertices.push( new THREE.Vector3() );
+			geometry.vertices.push( new THREE.Vector3() );
 
 		}
 
+	}
 
-		// update positions and sort
+	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
 
-		for ( var i = 0, l = sprites.length; i < l; i ++ ) {
+	this.matrixAutoUpdate = false;
 
-			var sprite = sprites[ i ];
+	this.update();
 
-			sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );
+};
 
-			if ( sprite.renderDepth === null ) {
+THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype );
 
-				sprite.z = - sprite._modelViewMatrix.elements[ 14 ];
+THREE.VertexTangentsHelper.prototype.update = ( function ( object ) {
 
-			} else {
+	var v1 = new THREE.Vector3();
 
-				sprite.z = sprite.renderDepth;
+	return function( object ) {
+
+		var keys = [ 'a', 'b', 'c', 'd' ];
+
+		this.object.updateMatrixWorld( true );
+
+		var vertices = this.geometry.vertices;
+
+		var verts = this.object.geometry.vertices;
 
-			}
+		var faces = this.object.geometry.faces;
 
-		}
+		var worldMatrix = this.object.matrixWorld;
 
-		sprites.sort( painterSortStable );
+		var idx = 0;
 
-		// render all sprites
+		for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-		var scale = [];
+			var face = faces[ i ];
 
-		for ( var i = 0, l = sprites.length; i < l; i ++ ) {
+			for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) {
 
-			var sprite = sprites[ i ];
-			var material = sprite.material;
+				var vertexId = face[ keys[ j ] ];
+				var vertex = verts[ vertexId ];
 
-			gl.uniform1f( uniforms.alphaTest, material.alphaTest );
-			gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements );
+				var tangent = face.vertexTangents[ j ];
 
-			scale[ 0 ] = sprite.scale.x;
-			scale[ 1 ] = sprite.scale.y;
+				vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix );
 
-			var fogType = 0;
+				v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size );
 
-			if ( scene.fog && material.fog ) {
+				v1.add( vertices[ idx ] );
+				idx = idx + 1;
 
-				fogType = sceneFogType;
+				vertices[ idx ].copy( v1 );
+				idx = idx + 1;
 
 			}
 
-			if ( oldFogType !== fogType ) {
+		}
 
-				gl.uniform1i( uniforms.fogType, fogType );
-				oldFogType = fogType;
+		this.geometry.verticesNeedUpdate = true;
 
-			}
+		return this;
 
-			if ( material.map !== null ) {
+	}
 
-				gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y );
-				gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y );
+}());
 
-			} else {
+// File:src/extras/helpers/WireframeHelper.js
 
-				gl.uniform2f( uniforms.uvOffset, 0, 0 );
-				gl.uniform2f( uniforms.uvScale, 1, 1 );
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
 
-			}
+THREE.WireframeHelper = function ( object, hex ) {
 
-			gl.uniform1f( uniforms.opacity, material.opacity );
-			gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );
+	var color = ( hex !== undefined ) ? hex : 0xffffff;
 
-			gl.uniform1f( uniforms.rotation, material.rotation );
-			gl.uniform2fv( uniforms.scale, scale );
+	var edge = [ 0, 0 ], hash = {};
+	var sortFunction = function ( a, b ) { return a - b };
 
-			renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
-			renderer.setDepthTest( material.depthTest );
-			renderer.setDepthWrite( material.depthWrite );
+	var keys = [ 'a', 'b', 'c' ];
+	var geometry = new THREE.BufferGeometry();
 
-			if ( material.map && material.map.image && material.map.image.width ) {
+	if ( object.geometry instanceof THREE.Geometry ) {
 
-				renderer.setTexture( material.map, 0 );
+		var vertices = object.geometry.vertices;
+		var faces = object.geometry.faces;
+		var numEdges = 0;
 
-			} else {
+		// allocate maximal size
+		var edges = new Uint32Array( 6 * faces.length );
 
-				renderer.setTexture( texture, 0 );
+		for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-			}
+			var face = faces[ i ];
 
-			gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
+			for ( var j = 0; j < 3; j ++ ) {
 
-		}
+				edge[ 0 ] = face[ keys[ j ] ];
+				edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
+				edge.sort( sortFunction );
 
-		// restore gl
+				var key = edge.toString();
 
-		gl.enable( gl.CULL_FACE );
-		
-		renderer.resetGLState();
+				if ( hash[ key ] === undefined ) {
 
-	};
+					edges[ 2 * numEdges ] = edge[ 0 ];
+					edges[ 2 * numEdges + 1 ] = edge[ 1 ];
+					hash[ key ] = true;
+					numEdges ++;
 
-	function createProgram () {
+				}
 
-		var program = gl.createProgram();
+			}
 
-		var vertexShader = gl.createShader( gl.VERTEX_SHADER );
-		var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
+		}
 
-		gl.shaderSource( vertexShader, [
+		var coords = new Float32Array( numEdges * 2 * 3 );
 
-			'precision ' + renderer.getPrecision() + ' float;',
+		for ( var i = 0, l = numEdges; i < l; i ++ ) {
 
-			'uniform mat4 modelViewMatrix;',
-			'uniform mat4 projectionMatrix;',
-			'uniform float rotation;',
-			'uniform vec2 scale;',
-			'uniform vec2 uvOffset;',
-			'uniform vec2 uvScale;',
+			for ( var j = 0; j < 2; j ++ ) {
 
-			'attribute vec2 position;',
-			'attribute vec2 uv;',
+				var vertex = vertices[ edges [ 2 * i + j] ];
 
-			'varying vec2 vUV;',
+				var index = 6 * i + 3 * j;
+				coords[ index + 0 ] = vertex.x;
+				coords[ index + 1 ] = vertex.y;
+				coords[ index + 2 ] = vertex.z;
 
-			'void main() {',
+			}
 
-				'vUV = uvOffset + uv * uvScale;',
+		}
 
-				'vec2 alignedPosition = position * scale;',
+		geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
 
-				'vec2 rotatedPosition;',
-				'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',
-				'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',
+	} else if ( object.geometry instanceof THREE.BufferGeometry ) {
 
-				'vec4 finalPosition;',
+		if ( object.geometry.attributes.index !== undefined ) { // Indexed BufferGeometry
 
-				'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',
-				'finalPosition.xy += rotatedPosition;',
-				'finalPosition = projectionMatrix * finalPosition;',
+			var vertices = object.geometry.attributes.position.array;
+			var indices = object.geometry.attributes.index.array;
+			var drawcalls = object.geometry.drawcalls;
+			var numEdges = 0;
 
-				'gl_Position = finalPosition;',
+			if ( drawcalls.length === 0 ) {
 
-			'}'
+				drawcalls = [ { count : indices.length, index : 0, start : 0 } ];
 
-		].join( '\n' ) );
+			}
 
-		gl.shaderSource( fragmentShader, [
+			// allocate maximal size
+			var edges = new Uint32Array( 2 * indices.length );
 
-			'precision ' + renderer.getPrecision() + ' float;',
+			for ( var o = 0, ol = drawcalls.length; o < ol; ++ o ) {
 
-			'uniform vec3 color;',
-			'uniform sampler2D map;',
-			'uniform float opacity;',
+				var start = drawcalls[ o ].start;
+				var count = drawcalls[ o ].count;
+				var index = drawcalls[ o ].index;
 
-			'uniform int fogType;',
-			'uniform vec3 fogColor;',
-			'uniform float fogDensity;',
-			'uniform float fogNear;',
-			'uniform float fogFar;',
-			'uniform float alphaTest;',
+				for ( var i = start, il = start + count; i < il; i += 3 ) {
 
-			'varying vec2 vUV;',
+					for ( var j = 0; j < 3; j ++ ) {
 
-			'void main() {',
+						edge[ 0 ] = index + indices[ i + j ];
+						edge[ 1 ] = index + indices[ i + ( j + 1 ) % 3 ];
+						edge.sort( sortFunction );
 
-				'vec4 texture = texture2D( map, vUV );',
+						var key = edge.toString();
 
-				'if ( texture.a < alphaTest ) discard;',
+						if ( hash[ key ] === undefined ) {
 
-				'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',
+							edges[ 2 * numEdges ] = edge[ 0 ];
+							edges[ 2 * numEdges + 1 ] = edge[ 1 ];
+							hash[ key ] = true;
+							numEdges ++;
 
-				'if ( fogType > 0 ) {',
+						}
 
-					'float depth = gl_FragCoord.z / gl_FragCoord.w;',
-					'float fogFactor = 0.0;',
+					}
 
-					'if ( fogType == 1 ) {',
+				}
 
-						'fogFactor = smoothstep( fogNear, fogFar, depth );',
+			}
 
-					'} else {',
+			var coords = new Float32Array( numEdges * 2 * 3 );
 
-						'const float LOG2 = 1.442695;',
-						'float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );',
-						'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',
+			for ( var i = 0, l = numEdges; i < l; i ++ ) {
 
-					'}',
+				for ( var j = 0; j < 2; j ++ ) {
 
-					'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );',
+					var index = 6 * i + 3 * j;
+					var index2 = 3 * edges[ 2 * i + j];
+					coords[ index + 0 ] = vertices[ index2 ];
+					coords[ index + 1 ] = vertices[ index2 + 1 ];
+					coords[ index + 2 ] = vertices[ index2 + 2 ];
 
-				'}',
+				}
 
-			'}'
+			}
 
-		].join( '\n' ) );
+			geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
 
-		gl.compileShader( vertexShader );
-		gl.compileShader( fragmentShader );
+		} else { // non-indexed BufferGeometry
 
-		gl.attachShader( program, vertexShader );
-		gl.attachShader( program, fragmentShader );
+			var vertices = object.geometry.attributes.position.array;
+			var numEdges = vertices.length / 3;
+			var numTris = numEdges / 3;
 
-		gl.linkProgram( program );
+			var coords = new Float32Array( numEdges * 2 * 3 );
 
-		return program;
+			for ( var i = 0, l = numTris; i < l; i ++ ) {
 
-	};
+				for ( var j = 0; j < 3; j ++ ) {
 
-	function painterSortStable ( a, b ) {
+					var index = 18 * i + 6 * j;
 
-		if ( a.z !== b.z ) {
+					var index1 = 9 * i + 3 * j;
+					coords[ index + 0 ] = vertices[ index1 ];
+					coords[ index + 1 ] = vertices[ index1 + 1 ];
+					coords[ index + 2 ] = vertices[ index1 + 2 ];
 
-			return b.z - a.z;
+					var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 );
+					coords[ index + 3 ] = vertices[ index2 ];
+					coords[ index + 4 ] = vertices[ index2 + 1 ];
+					coords[ index + 5 ] = vertices[ index2 + 2 ];
 
-		} else {
+				}
 
-			return b.id - a.id;
+			}
+
+			geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
 
 		}
 
-	};
+	}
+
+	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces );
+
+	this.matrix = object.matrixWorld;
+	this.matrixAutoUpdate = false;
 
 };
 
-// File:src/extras/renderers/plugins/DepthPassPlugin.js
+THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype );
+
+// File:src/extras/objects/ImmediateRenderObject.js
 
 /**
  * @author alteredq / http://alteredqualia.com/
  */
 
-THREE.DepthPassPlugin = function () {
-
-	this.enabled = false;
-	this.renderTarget = null;
-
-	var _gl,
-	_renderer,
-	_lights, _webglObjects, _webglObjectsImmediate,
-	_depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
-
-	_frustum = new THREE.Frustum(),
-	_projScreenMatrix = new THREE.Matrix4(),
-	_renderList = [];
+THREE.ImmediateRenderObject = function () {
 
-	this.init = function ( renderer, lights, webglObjects, webglObjectsImmediate ) {
+	THREE.Object3D.call( this );
 
-		_gl = renderer.context;
-		_renderer = renderer;
-		_lights = lights;
+	this.render = function ( renderCallback ) {};
 
-		_webglObjects = webglObjects;
-		_webglObjectsImmediate = webglObjectsImmediate;
+};
 
-		var depthShader = THREE.ShaderLib[ "depthRGBA" ];
-		var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
+THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype );
 
-		_depthMaterial = new THREE.ShaderMaterial( {
-			fragmentShader: depthShader.fragmentShader,
-			vertexShader: depthShader.vertexShader,
-			uniforms: depthUniforms
-		} );
-		
-		_depthMaterialMorph = new THREE.ShaderMaterial( {
-			fragmentShader: depthShader.fragmentShader,
-			vertexShader: depthShader.vertexShader,
-			uniforms: depthUniforms,
-			morphTargets: true
-		} );
-		
-		_depthMaterialSkin = new THREE.ShaderMaterial( {
-			fragmentShader: depthShader.fragmentShader,
-			vertexShader: depthShader.vertexShader,
-			uniforms: depthUniforms,
-			skinning: true
-		} );
-		
-		_depthMaterialMorphSkin = new THREE.ShaderMaterial( {
-			fragmentShader: depthShader.fragmentShader,
-			vertexShader: depthShader.vertexShader,
-			uniforms: depthUniforms,
-			morphTargets: true,
-			skinning: true
-		} );
+// File:src/extras/objects/MorphBlendMesh.js
 
-		_depthMaterial._shadowPass = true;
-		_depthMaterialMorph._shadowPass = true;
-		_depthMaterialSkin._shadowPass = true;
-		_depthMaterialMorphSkin._shadowPass = true;
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
 
-	};
+THREE.MorphBlendMesh = function( geometry, material ) {
 
-	this.render = function ( scene, camera ) {
+	THREE.Mesh.call( this, geometry, material );
 
-		if ( ! this.enabled ) return;
+	this.animationsMap = {};
+	this.animationsList = [];
 
-		this.update( scene, camera );
+	// prepare default animation
+	// (all frames played together in 1 second)
 
-	};
+	var numFrames = this.geometry.morphTargets.length;
 
-	this.update = function ( scene, camera ) {
+	var name = "__default";
 
-		var i, il, j, jl, n,
+	var startFrame = 0;
+	var endFrame = numFrames - 1;
 
-		program, buffer, material,
-		webglObject, object, light,
-		renderList,
+	var fps = numFrames / 1;
 
-		fog = null;
+	this.createAnimation( name, startFrame, endFrame, fps );
+	this.setAnimationWeight( name, 1 );
 
-		// set GL state for depth map
+};
 
-		_gl.clearColor( 1, 1, 1, 1 );
-		_gl.disable( _gl.BLEND );
+THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype );
 
-		_renderer.setDepthTest( true );
+THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) {
 
-		// update scene
+	var animation = {
 
-		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+		startFrame: start,
+		endFrame: end,
 
-		// update camera matrices and frustum
+		length: end - start + 1,
 
-		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+		fps: fps,
+		duration: ( end - start ) / fps,
 
-		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
-		_frustum.setFromMatrix( _projScreenMatrix );
+		lastFrame: 0,
+		currentFrame: 0,
 
-		// render depth map
+		active: false,
 
-		_renderer.setRenderTarget( this.renderTarget );
-		_renderer.clear();
+		time: 0,
+		direction: 1,
+		weight: 1,
 
-		// set object matrices & frustum culling
-		
-		_renderList.length = 0;
-		projectObject(scene,scene,camera);
+		directionBackwards: false,
+		mirroredLoop: false
 
-		// render regular objects
+	};
 
-		var objectMaterial, useMorphing, useSkinning;
+	this.animationsMap[ name ] = animation;
+	this.animationsList.push( animation );
 
-		for ( j = 0, jl = _renderList.length; j < jl; j ++ ) {
+};
 
-			webglObject = _renderList[ j ];
+THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) {
 
-			object = webglObject.object;
-			buffer = webglObject.buffer;
+	var pattern = /([a-z]+)_?(\d+)/;
 
-			// todo: create proper depth material for particles
+	var firstAnimation, frameRanges = {};
 
-			if ( object instanceof THREE.PointCloud && ! object.customDepthMaterial ) continue;
+	var geometry = this.geometry;
 
-			objectMaterial = getObjectMaterial( object );
+	for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
 
-			if ( objectMaterial ) _renderer.setMaterialFaces( object.material );
+		var morph = geometry.morphTargets[ i ];
+		var chunks = morph.name.match( pattern );
 
-			useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
-			useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning;
+		if ( chunks && chunks.length > 1 ) {
 
-			if ( object.customDepthMaterial ) {
+			var name = chunks[ 1 ];
+			var num = chunks[ 2 ];
 
-				material = object.customDepthMaterial;
+			if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity };
 
-			} else if ( useSkinning ) {
+			var range = frameRanges[ name ];
 
-				material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;
+			if ( i < range.start ) range.start = i;
+			if ( i > range.end ) range.end = i;
 
-			} else if ( useMorphing ) {
+			if ( ! firstAnimation ) firstAnimation = name;
 
-				material = _depthMaterialMorph;
+		}
 
-			} else {
+	}
 
-				material = _depthMaterial;
+	for ( var name in frameRanges ) {
 
-			}
+		var range = frameRanges[ name ];
+		this.createAnimation( name, range.start, range.end, fps );
 
-			if ( buffer instanceof THREE.BufferGeometry ) {
+	}
 
-				_renderer.renderBufferDirect( camera, _lights, fog, material, buffer, object );
+	this.firstAnimation = firstAnimation;
 
-			} else {
+};
 
-				_renderer.renderBuffer( camera, _lights, fog, material, buffer, object );
+THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) {
 
-			}
+	var animation = this.animationsMap[ name ];
 
+	if ( animation ) {
 
-		}
+		animation.direction = 1;
+		animation.directionBackwards = false;
 
-		// set matrices and render immediate objects
+	}
 
-		for ( j = 0, jl = _webglObjectsImmediate.length; j < jl; j ++ ) {
+};
 
-			webglObject = _webglObjectsImmediate[ j ];
-			object = webglObject.object;
+THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) {
 
-			if ( object.visible ) {
+	var animation = this.animationsMap[ name ];
 
-				object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+	if ( animation ) {
 
-				_renderer.renderImmediateObject( camera, _lights, fog, _depthMaterial, object );
+		animation.direction = - 1;
+		animation.directionBackwards = true;
 
-			}
+	}
 
-		}
+};
 
-		// restore GL state
+THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) {
 
-		var clearColor = _renderer.getClearColor(),
-		clearAlpha = _renderer.getClearAlpha();
+	var animation = this.animationsMap[ name ];
 
-		_gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
-		_gl.enable( _gl.BLEND );
+	if ( animation ) {
 
-	};
-	
-	function projectObject(scene, object,camera){
-		
-		if ( object.visible ) {
-	
-			var webglObjects = _webglObjects[object.id];
-	
-			if (webglObjects && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) {
-		
-		
-				for (var i = 0, l = webglObjects.length; i < l; i++){
-			
-					var webglObject = webglObjects[i];
-					
-					object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
-					_renderList.push(webglObject);
-					
-				}
-			}
-	
-			for(var i = 0, l = object.children.length; i < l; i++) {
-				
-				projectObject(scene, object.children[i], camera);
-			}
-		
-		}
-	}
+		animation.fps = fps;
+		animation.duration = ( animation.end - animation.start ) / animation.fps;
 
-	// For the moment just ignore objects that have multiple materials with different animation methods
-	// Only the first material will be taken into account for deciding which depth material to use
+	}
 
-	function getObjectMaterial( object ) {
+};
 
-		return object.material instanceof THREE.MeshFaceMaterial
-			? object.material.materials[ 0 ]
-			: object.material;
+THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) {
 
-	};
+	var animation = this.animationsMap[ name ];
 
-};
+	if ( animation ) {
 
+		animation.duration = duration;
+		animation.fps = ( animation.end - animation.start ) / animation.duration;
 
-// File:src/extras/shaders/ShaderFlares.js
+	}
 
-/**
- * @author mikael emtinger / http://gomo.se/
- */
+};
 
-THREE.ShaderFlares = {
+THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) {
 
-	'lensFlareVertexTexture': {
+	var animation = this.animationsMap[ name ];
 
-		vertexShader: [
+	if ( animation ) {
 
-			"uniform lowp int renderType;",
+		animation.weight = weight;
 
-			"uniform vec3 screenPosition;",
-			"uniform vec2 scale;",
-			"uniform float rotation;",
+	}
 
-			"uniform sampler2D occlusionMap;",
+};
 
-			"attribute vec2 position;",
-			"attribute vec2 uv;",
+THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) {
 
-			"varying vec2 vUV;",
-			"varying float vVisibility;",
+	var animation = this.animationsMap[ name ];
 
-			"void main() {",
+	if ( animation ) {
 
-				"vUV = uv;",
+		animation.time = time;
 
-				"vec2 pos = position;",
+	}
 
-				"if( renderType == 2 ) {",
+};
 
-					"vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );",
-					"visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );",
-					"visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );",
-					"visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );",
-					"visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );",
-					"visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );",
-					"visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );",
-					"visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );",
-					"visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );",
+THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) {
 
-					"vVisibility =        visibility.r / 9.0;",
-					"vVisibility *= 1.0 - visibility.g / 9.0;",
-					"vVisibility *=       visibility.b / 9.0;",
-					"vVisibility *= 1.0 - visibility.a / 9.0;",
+	var time = 0;
 
-					"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
-					"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
+	var animation = this.animationsMap[ name ];
 
-				"}",
+	if ( animation ) {
 
-				"gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
+		time = animation.time;
 
-			"}"
+	}
 
-		].join( "\n" ),
+	return time;
 
-		fragmentShader: [
+};
 
-			"uniform lowp int renderType;",
+THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) {
 
-			"uniform sampler2D map;",
-			"uniform float opacity;",
-			"uniform vec3 color;",
+	var duration = - 1;
 
-			"varying vec2 vUV;",
-			"varying float vVisibility;",
+	var animation = this.animationsMap[ name ];
 
-			"void main() {",
+	if ( animation ) {
 
-				// pink square
+		duration = animation.duration;
 
-				"if( renderType == 0 ) {",
+	}
 
-					"gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
+	return duration;
 
-				// restore
+};
 
-				"} else if( renderType == 1 ) {",
+THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) {
 
-					"gl_FragColor = texture2D( map, vUV );",
+	var animation = this.animationsMap[ name ];
 
-				// flare
+	if ( animation ) {
 
-				"} else {",
+		animation.time = 0;
+		animation.active = true;
 
-					"vec4 texture = texture2D( map, vUV );",
-					"texture.a *= opacity * vVisibility;",
-					"gl_FragColor = texture;",
-					"gl_FragColor.rgb *= color;",
+	} else {
 
-				"}",
+		console.warn( "animation[" + name + "] undefined" );
 
-			"}"
-		].join( "\n" )
+	}
 
-	},
+};
 
+THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) {
 
-	'lensFlare': {
+	var animation = this.animationsMap[ name ];
 
-		vertexShader: [
+	if ( animation ) {
 
-			"uniform lowp int renderType;",
+		animation.active = false;
 
-			"uniform vec3 screenPosition;",
-			"uniform vec2 scale;",
-			"uniform float rotation;",
+	}
 
-			"attribute vec2 position;",
-			"attribute vec2 uv;",
+};
 
-			"varying vec2 vUV;",
+THREE.MorphBlendMesh.prototype.update = function ( delta ) {
 
-			"void main() {",
+	for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) {
 
-				"vUV = uv;",
+		var animation = this.animationsList[ i ];
 
-				"vec2 pos = position;",
+		if ( ! animation.active ) continue;
 
-				"if( renderType == 2 ) {",
+		var frameTime = animation.duration / animation.length;
 
-					"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
-					"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
+		animation.time += animation.direction * delta;
 
-				"}",
+		if ( animation.mirroredLoop ) {
 
-				"gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
+			if ( animation.time > animation.duration || animation.time < 0 ) {
 
-			"}"
+				animation.direction *= - 1;
 
-		].join( "\n" ),
+				if ( animation.time > animation.duration ) {
 
-		fragmentShader: [
+					animation.time = animation.duration;
+					animation.directionBackwards = true;
 
-			"precision mediump float;",
+				}
 
-			"uniform lowp int renderType;",
+				if ( animation.time < 0 ) {
 
-			"uniform sampler2D map;",
-			"uniform sampler2D occlusionMap;",
-			"uniform float opacity;",
-			"uniform vec3 color;",
+					animation.time = 0;
+					animation.directionBackwards = false;
 
-			"varying vec2 vUV;",
+				}
 
-			"void main() {",
+			}
 
-				// pink square
+		} else {
 
-				"if( renderType == 0 ) {",
+			animation.time = animation.time % animation.duration;
 
-					"gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );",
+			if ( animation.time < 0 ) animation.time += animation.duration;
 
-				// restore
+		}
 
-				"} else if( renderType == 1 ) {",
+		var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 );
+		var weight = animation.weight;
 
-					"gl_FragColor = texture2D( map, vUV );",
+		if ( keyframe !== animation.currentFrame ) {
 
-				// flare
+			this.morphTargetInfluences[ animation.lastFrame ] = 0;
+			this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight;
 
-				"} else {",
+			this.morphTargetInfluences[ keyframe ] = 0;
 
-					"float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;",
-					"visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;",
-					"visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;",
-					"visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;",
-					"visibility = ( 1.0 - visibility / 4.0 );",
+			animation.lastFrame = animation.currentFrame;
+			animation.currentFrame = keyframe;
 
-					"vec4 texture = texture2D( map, vUV );",
-					"texture.a *= opacity * visibility;",
-					"gl_FragColor = texture;",
-					"gl_FragColor.rgb *= color;",
+		}
 
-				"}",
+		var mix = ( animation.time % frameTime ) / frameTime;
 
-			"}"
+		if ( animation.directionBackwards ) mix = 1 - mix;
 
-		].join( "\n" )
+		this.morphTargetInfluences[ animation.currentFrame ] = mix * weight;
+		this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight;
 
 	}
 

File diff suppressed because it is too large
+ 27 - 29
build/three.min.js


Some files were not shown because too many files changed in this diff